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.
Related
I have a slide box inside a modal.
The slide box used to be inside the modal template, with the relevant functions inside a controller.
I realised that it was better to move it inside a directive, and move all the functions inside the directive's link function.
So, here's what I did:
The modal template calls only the directive: <my-directive></my-directive> and nothing else.
The directive code is the following:
angular.module('feedback', []).directive('myDirective', [
'$ionicSlideBoxDelegate', '$rootScope', 'Info', function($ionicSlideBoxDelegate, $rootScope, Info) {
var linkFunction;
linkFunction = function(scope) {
scope.data = {
timestamp: new Date,
details: {
app: Info.getAppInfo(),
device: Info.getDeviceInfo()
},
user: $rootScope.currentUser,
message: ''
};
scope.slideChanged = function(index) {
scope.slideIndex = index;
};
scope.disableSwipe = function() {
$ionicSlideBoxDelegate.enableSlide(true);
};
scope.slideTo = function(index) {
console.log("Got here...");
console.log("Index is: ", index);
$ionicSlideBoxDelegate.slide(index);
};
};
return {
restrict: 'E',
replace: false,
templateUrl: './directives/feedback/feedback.tpl.html',
link: linkFunction
};
}
]);
and the template is the following:
<div>
<ion-slide-box ng-init="disableSwipe()" on-slide-changed="slideChanged(index)" class="feedback-slider" show-pager="false">
<ion-slide>
<ion-item class="item-icon item-icon-right" ng-click="slideTo(1)">
</ion-slide>
<ion-slide>
<button class="button button-positive button-clear no-animation"
ng-click="slideTo(0)">Back</button>
</ion-slide>
</ion-slide-box>
</div>
This is a trip down version of the template, but it should be enough to explain what it's going on.
basically, none of the functions are called. I mean, they are called as the consoles are printed and are displaying the expected values, but it seams like everything related to $ionicSlideBoxDelegate are not fired, and I really don't understand why.
Any help?
Thanks
So,
After a lot of research and trying to debug the ionic function, I managed to solve this using the ionic forum at this link:
https://github.com/driftyco/ionic/issues/1865
Seams like the problem is when you have multiple sliders on you app, and it is a known issue within the ionic library.
I change my code like this:
$ionicSlideBoxDelegate.$getByHandle('feedbackDirective')._instances[0].enableSlide(false);
and added an handler in the html template as well:
<ion-slide-box ng-init="disableSwipe()"
on-slide-changed="slideChanged(index)"
class="hg-feedback-slider"
show-pager="false"
delegate-handle="feedbackDirective">
...
and now it's working properly again.
I am using AngularJS Modal Service. http://fundoo-solutions.github.io/angularjs-modal-service/
I setup it in a simple way
Button to open a Model
<div data-ng-controller="contest as vm">
<a class="btn btn-primary" data-ng-click="vm.createFileUploadDialog()">Upload Image</a>
</div>
Inisde Controller I have a function defined createFileUploadDialog and expose it from my viewModel.
vm.createFileUploadDialog = createFileUploadDialog;
vm.uploadme = {};
vm.uploadme.src = "";
function createFileUploadDialog() {
createDialog('/app/templates/fileuploadDialog.html', {
id: 'filuploadDialog',
title: 'Upload Contest Image',
backdrop: true,
success: { label: 'Upload', fn: uploadSuccess },
cancel: { label: 'Cancel' },
});
}
function uploadSuccess() {
console.log(vm.uploadme);
//need to call to the backend
}
And inside "fileUploadDialog.html" I have a simple markup
<div>
<input type="file" fileread="uploadme.src" />
</div>
"fileread" is a directive which return back the src of the File. Now the problem I have
As you can see I am doing console.log inside "UploadSuccess", in response I am getting the result "Object {src: ""}",
It looks like the Modal values not capture inside controller. But If I do the same with $rootScope, it logs out the File that need to upload. So, how can I access the value without using $rootScope? Please suggest
PS:
I am not define separate controller for Modal, want to use the same controller that treats my view.
** Modals scope is not the same as your controller scope!**
if you want to see your Controller scope inside of your modal and manupulate it , you're gonna have to use resolve inside of your modal markap like this :
createDialog('/app/templates/fileuploadDialog.html', {
id: 'filuploadDialog',
title: 'Upload Contest Image',
backdrop: true,
success: { label: 'Upload', fn: uploadSuccess },
cancel: { label: 'Cancel' },
resolve:{
controllerscope:function(){
return $scope;
}
}
});
And now , inside of your modal controller you can inject :** controllerscope ** and use it , also data binding works well like this :
app.controller('modalcontroller',function($scope,controllerscope){
// no you have access to your controller scope with **controllerscope**
})
So go and have a look at your modal plug in wich you are using and search for resolve and controller
thats it
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 ;)
Obviously this is caused by me being new to AngularJS, but I don't know what is the problem.
Basically, I have a list of items and an input control for filtering the list that is located in a pop out side drawer.
That works perfectly until I added a directive to set focus to that input control when it becomes visible. Then the focus works, but the filter stops working. No errors. Removing focus="{{open}}" from the markup makes the filter work.
The focus method was taken from this StackOverflow post:
How to set focus on input field?
Here is the code...
/* impersonate.html */
<section class="impersonate">
<div header></div>
<ul>
<li ng-repeat="item in items | filter:search">{{item.name}}</li>
</ul>
<div class="handle handle-right icon-search" tap="toggle()"></div>
<div class="drawer drawer-right"
ng-class="{expanded: open, collapsed: !open}">
Search<br />
<input class="SearchBox" ng-model="search.name"
focus="{{open}}" type="text">
</div>
</section>
// impersonateController.js
angular
.module('sales')
.controller(
'ImpersonateController',
[
'$scope',
function($scope) {
$scope.open = false;
$scope.toggle = function () {
$scope.open = !$scope.open;
}
}]
);
// app.js
angular
.module('myApp')
.directive('focus', function($timeout) {
return {
scope: { trigger: '#focus' },
link: function(scope, element) {
scope.$watch('trigger', function(value) {
if(value === "true") {
console.log('trigger',value);
$timeout(function() {
element[0].focus();
});
}
});
}
};
})
Any assistance would be greatly appreciated!
Thanks!
Thad
The focus directive uses an isolated scope.
scope: { trigger: '#focus' },
So, by adding the directive to the input-tag, ng-model="search.name" no longer points to ImpersonateController but to this new isolated scope.
Instead try:
ng-model="$parent.search.name"
demo: http://jsbin.com/ogexem/3/
P.s.: next time, please try to post copyable code. I had to make quite a lot of assumptions of how all this should be wired up.
Does anyone have any experience using KendoUI's window with AngularJS?
I'm currently using Angular-Kendo but I'm not entirely sure hot to cleanly use the window. Or if there is any other solutions for presenting a modal dialog and filling it with a form loaded via a partial I'm open to that as well.
My current code looks something like this:
HTML:
<div kendo-window id="addWindow" ng-hidden></div>
JS:
$scope.addSection = function() {
$("#addWindow").data("kendoWindow").open();
return false;
};
But I hate this, and it feels wrong for how I'm doing everything else. Any thoughts on a better way?
Check out this blog post:
http://www.kendoui.com/blogs/teamblog/posts/13-06-24/announcing-angular-kendo-ui.aspx?utm_content=bufferbbe83&utm_source=buffer&utm_medium=twitter&utm_campaign=Buffer
They rewrote Angular-Kendo and have an example of a clean way to use a window.
#anise thanks for ur information
finally i also resolve the issue.
Controller
$scope.window;
$scope.OpenWindow= function() // custom function on click
{
$scope.DlgOptions = {
width: 550,
height: 400,
visible: false,
actions: [
"Maximize",
"Close"
]
};
$scope.window.setOptions($scope.DlgOptions);
$scope.window.center(); // open dailog in center of screen
$scope.window.open();
};
View
<div kendo-window="window" k-visible="false" k-modal="true"> </div>
Check out this library
https://github.com/kjartanvalur/angular-kendo-window
var windowInstance = $kWindow.open({
options:{
modal: true,
title: "Window title",
width: 400,
},
templateUrl: 'modal1.html',
controller: 'modalController',
resolve: {
parameter1: function () {
return "Test...";
}
}
});
windowInstance.result.then(function (result) {
// Here you can get result from the window
});