Ionic: Accessing from object in controller of the custom modal form - angularjs

I've created a codepen to show the problem.
When the service appModalService is used, the vm object is directly replaced with the FormController object, so none of the attributes of vm is accessible in the template and the controller associated with the vm object becomes totally useless.
The discussion(reasoning) on the appModalService can be found in ionic form.
I've added the code here for reference. Any suggestions on fixing this issue ?
HTML:
<html ng-app="ionicApp">
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title>Ionic modal service</title>
<link href="http://code.ionicframework.com/nightly/css/ionic.css" rel="stylesheet">
<script src="http://code.ionicframework.com/nightly/js/ionic.bundle.js"></script>
</head>
<body ng-controller="AppCtrl as vm">
<ion-content padding="true" ng-class="{'has-footer': showFooter}">
<div class="row">
<div class="col">
<button ng-click="vm.showNonWorkingForm()" class="button button-assertive button-block">
Show Non Working Form (using appModalService)
</button>
</div>
</div>
<div class="row">
<div class="col">
<button ng-click="vm.showWorkingForm()" class="button button-assertive button-block">
Show Working Form (using ionic ModalService)
</button>
</div>
</div>
</ion-content>
<script id="non-working-form-modal.html" type="text/ng-template">
<ion-modal-view>
<ion-header-bar class="bar bar-header bar-positive">
<h1 class="title">Form</h1>
<button class="button button-clear button-primary" ng-click="vm.closeModal()">Close</button>
</ion-header-bar>
<ion-content>
<form novalidate name="vm.f" ng-submit="vm.submit()">
<div class="list">
<label class="item item-input">
<input placeholder="text" type="text" name="sometext" ng-model="vm.sometext" required>
</label>
</div>
<button type="submit" class="button button-block">
Submit
</button>
</form>
{{ vm }}
</ion-content>
</ion-modal-view>
</script>
<script id="working-form-modal.html" type="text/ng-template">
<div class="modal" ng-controller="WorkingCtrl as vm">
<ion-header-bar class="bar bar-header bar-positive">
<h1 class="title">Form</h1>
<button class="button button-clear button-primary" ng-click="closeModal()">Close</button>
</ion-header-bar>
<ion-content>
<form novalidate name="vm.f" ng-submit="vm.submit()">
<div class="list">
<label class="item item-input">
<input placeholder="text" type="text" name="sometext" ng-model="vm.sometext" required>
</label>
</div>
<button type="submit" class="button button-block">
Submit
</button>
</form>
{{ vm }}
</ion-content>
</div>
</script>
</body>
</html>
JS
angular.module('ionicApp', ['ionic'])
.controller('NonWorkingCtrl', ['$scope', 'parameters', function($scope, parameters) {
var vm = this;
/* placeholder for the FormController object */
vm.f = null;
vm.sometext = 'Added Some text';
vm.submit = function() {
if (vm.f.$valid) {
alert('NonWorkingCtrl Valid');
} else {
alert('NonWorkingCtrl InValid');
}
}
/* additional fields */
vm.field1 = 'field1';
vm.field2 = 'field2';
vm.field3 = 'field3';
vm.field4 = 'field4';
vm.field5 = 'field5';
vm.field6 = 'field6';
vm.field7 = 'field7';
}])
.controller('WorkingCtrl', ['$scope', function($scope) {
var vm = this;
/* placeholder for the FormController object */
vm.f = null;
vm.sometext = 'Added Some text';
vm.submit = function() {
if (vm.f.$valid) {
alert('WorkingCtrl Valid');
} else {
alert('WorkingCtrl InValid');
}
}
/* additional fields */
vm.field1 = 'field1';
vm.field2 = 'field2';
vm.field3 = 'field3';
vm.field4 = 'field4';
vm.field5 = 'field5';
vm.field6 = 'field6';
vm.field7 = 'field7';
}])
.controller('AppCtrl', ['$scope', 'appModalService', '$ionicModal', function($scope, appModalService, $ionicModal) {
var vm = this;
vm.showNonWorkingForm = function() {
appModalService.show('non-working-form-modal.html', 'NonWorkingCtrl as vm');
};
vm.showWorkingForm = function() {
$ionicModal.fromTemplateUrl('working-form-modal.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
$scope.modal = modal;
$scope.modal.show();
});
$scope.closeModal = function() {
$scope.modal.hide();
$scope.modal.remove();
};
}
}])
.factory('appModalService', ['$ionicModal', '$rootScope', '$q', '$injector', '$controller', function($ionicModal, $rootScope, $q, $injector, $controller) {
return {
show: show
}
function show(templeteUrl, controller, parameters, options) {
// Grab the injector and create a new scope
var deferred = $q.defer(),
ctrlInstance,
modalScope = $rootScope.$new(),
thisScopeId = modalScope.$id,
defaultOptions = {
animation: 'slide-in-up',
focusFirstInput: false,
backdropClickToClose: true,
hardwareBackButtonClose: true,
modalCallback: null
};
options = angular.extend({}, defaultOptions, options);
$ionicModal.fromTemplateUrl(templeteUrl, {
scope: modalScope,
animation: options.animation,
focusFirstInput: options.focusFirstInput,
backdropClickToClose: options.backdropClickToClose,
hardwareBackButtonClose: options.hardwareBackButtonClose
}).then(function(modal) {
modalScope.modal = modal;
modalScope.openModal = function() {
modalScope.modal.show();
};
modalScope.closeModal = function(result) {
deferred.resolve(result);
modalScope.modal.hide();
};
modalScope.$on('modal.hidden', function(thisModal) {
if (thisModal.currentScope) {
var modalScopeId = thisModal.currentScope.$id;
if (thisScopeId === modalScopeId) {
deferred.resolve(null);
_cleanup(thisModal.currentScope);
}
}
});
// Invoke the controller
var locals = {
'$scope': modalScope,
'parameters': parameters
};
var ctrlEval = _evalController(controller);
ctrlInstance = $controller(controller, locals);
if (ctrlEval.isControllerAs) {
ctrlInstance.openModal = modalScope.openModal;
ctrlInstance.closeModal = modalScope.closeModal;
}
modalScope.modal.show()
.then(function() {
modalScope.$broadcast('modal.afterShow', modalScope.modal);
});
if (angular.isFunction(options.modalCallback)) {
options.modalCallback(modal);
}
}, function(err) {
deferred.reject(err);
});
return deferred.promise;
}
function _cleanup(scope) {
scope.$destroy();
if (scope.modal) {
scope.modal.remove();
}
}
function _evalController(ctrlName) {
var result = {
isControllerAs: false,
controllerName: '',
propName: ''
};
var fragments = (ctrlName || '').trim().split(/\s+/);
result.isControllerAs = fragments.length === 3 && (fragments[1] || '').toLowerCase() === 'as';
if (result.isControllerAs) {
result.controllerName = fragments[0];
result.propName = fragments[2];
} else {
result.controllerName = ctrlName;
}
return result;
}
}]);

#NEB I was having a similar issue around forms in ionic. I found my answer from DiscGolfer17 on the IonicForum, here
Basically, the ionic's <content> directive creates it's own isolated scope. As do forms and ionic's <ion-modal-view> directive I believe.
So to get your ng-click="vm.closeModal()" to work you need to chain a $parent in front of vm like this ng-click="$parent.vm.closeModal()"
This tells it to inherit it's parents $scope. Here is a working fork of your Plunk

Related

Update scope variable in directive not updating the visual

I have a simple slideshow. I introduced a new scope variable called 'isLoading'. The idea is to disable the Next button until the image is fully loaded. However the Next button remains disabled. The UI is not detecting that the scope variable has changed.
<html>
<link rel="stylesheet" href="bootstrap.min.css">
<script src='angular.min.js'></script>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.slides = [
{filename : 'Slide5.jpg', type : 'image'},
{filename : 'Slide1.PNG', type : 'image'},
];
$scope.page = 0;
setCurrentSlide();
$scope.previous = function() {
$scope.page--;
setCurrentSlide();
};
$scope.next = function() {
$scope.page++;
setCurrentSlide();
};
$scope.completed = function() {
alert('done');
};
function setCurrentSlide() {
$scope.isLoading = true;
$scope.currentSlide = $scope.slides[$scope.page];
}
});
app.directive('imgloader', [function () {
return {
restrict: 'A',
link: function (scope, ele) {
ele.bind('load', function () {
scope.isLoading = false;
});
}
};
}]);
</script>
<body ng-app="myApp" ng-controller="myCtrl">
<div class="slidecontainer">
<img imgloader ng-if="currentSlide.type == 'image'" src='{{currentSlide.filename}}'/>
<video ng-if="currentSlide.type == 'video'" controls>
<source src="{{currentSlide.filename}}"/>
</video>
</div>
</div>
<div class="controlscontainer">
<span class="slidenumbers">Slide {{page+1}} / {{slides.length}}</span>
<button ng-click="previous()" ng-disabled="page <= 0" class="btn btn-lg btn-primary">Previous</button>
<button ng-click="next()" ng-disabled="isLoading || page >= slides.length-1" class="btn btn-lg btn-primary">Next</button>
<button ng-click="completed()" ng-disabled="page != slides.length-1" class="btn btn-lg btn-success">Complete</button>
</div>
</body>
</html>
You are mixing the scope of your directive with the $scope of your controller:
notice that this 2 reffer to different contexts scope.isLoading !== $scope.isLoading
instead you colud try:
<img ng-load="isLoading = false" ng-if="currentSlide.type == 'image'" src='{{currentSlide.filename}}'/>
Where isLoading referes to the property on the $scope of your controller

Save Form data from Popover in Angularjs

var App = angular.module('myApp', []);
App.controller('myPopoverCtrl',
function($scope){
$scope.myPopover = {
isOpen: false,
open: function open() {
$scope.myPopover.isOpen = true;
},
close: function close() {
// alert('hi');
$scope.myPopover.isOpen = false;
}
};
$scope.SaveNotes = function() {
console.log('hi');
console.log($scope.noteText);
//getting undefined here
return false;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app = "App">
<a uib-popover-template="'AddNote.html'"
popover-title="AddNote"
popover-trigger="'outsideClick'"
ng-controller="myPopoverCtrl"
popover-is-open="myPopover.isOpen"
ng-click="myPopover.open()">Add
</a>
</div>
<script type="text/ng-template" id="AddNote.html">
<div>
<textarea class="form-control height-auto"
ng-model="noteText"
placeholder="This is a new note" ></textarea>
<input class="btn btn-outline btn-primary"
type="button"
ng- click="SaveNotes();" value="Save">
</div>
</script>
I have web a page where i have a button and when click the button popover appears.In that popover i have textarea but when i click save button i want get the text in my controller but i am getting undefined using $scope.modelname
How can i get that data?
I think you want to use a modal rather than a popover like so, as a popover is really just to display text :-
var modalInstance = $modal.open({
animation: $rootScope.animationsEnabled,
templateUrl: 'views/myTemplate.html',
size: 'md'
}).result.then(function(result) {
$scope.result = result;
});
This is what it's designed for more info here.

Having select tag in ui-bootstrap model doesn't display footer buttons

If I use a select tag (dropdown) in my ui-bootstrap modal body then it doesn't show modal-footer buttons. It's working fine if I use input element instead of select tag.
I have tried all the things and couldn't resolve this. May be there is something which is missing.
var app = angular.module('myApp', ['ngAnimate','ui.bootstrap']);
app.controller('shell', function($scope, $interval, $uibModal) {
$scope.test = 'hello world';
this.counter = 0;
var vm = this;
this.moveHandler = function() {
console.log('mouse moved, reset counter!');
vm.counter = 0;
};
var timer = function(iterCount) {
console.log('timer tick', vm.counter);
vm.counter++;
};
var intervalPromise = $interval(timer, 100);
$scope.$on("$destroy", function handler() {
// destruction code here
$interval.cancel(intervalPromise); // stop interval on destroy of ctrl.
});
$scope.items = ['item1', 'item2', 'item3'];
$scope.open = function (size) {
var modalInstance = $uibModal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function () {
return $scope.items;
}
}
});
};
});
// modal code from angular-ui-bootstrap demo
app.controller('ModalDemoCtrl', function ($scope, $uibModal, $log) {
$scope.items = ['item1', 'item2', 'item3'];
$scope.open = function (size) {
var modalInstance = $uibModal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function () {
return $scope.items;
}
}
});
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
});
// Please note that $modalInstance represents a modal window (instance) dependency.
// It is not the same as the $modal service used above.
app.controller('ModalInstanceCtrl', function ($scope, $uibModalInstance, items) {
$scope.items = items;
$scope.selected = {
item: $scope.items[0]
};
$scope.ok = function () {
$uibModalInstance.close($scope.selected.item);
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
});
html, body {
width: 100%;
height: 100%;
}
/*
.modal {
display: block;
}*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular-animate.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.3.0/ui-bootstrap-tpls.js"></script>
<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet"/>
<div ng-controller="ModalDemoCtrl">
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">
<h3 class="modal-title">I'm a modal!</h3>
</div>
<div class="modal-body">
<!-- <input type="text" class="form-control"/> -->
<select class="form-control"/>
</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>
</script>
<button class="btn btn-default" ng-click="open()">Open me!</button>
<button class="btn btn-default" ng-click="open('lg')">Large modal</button>
<button class="btn btn-default" ng-click="open('sm')">Small modal</button>
<div ng-show="selected">Selection from a modal: {{ selected }}</div>
{{test}}
</div>
enter image description here
Hi u have to close the select tag instead inline closing you have to close like that
Wrong
<select class="form-control"/>
Right
select class="form-control" ></select>
for reference https://plnkr.co/edit/E8Ztel80jhKCaZYIqpex
and checknow your Modal footer Appears

text boxes changed to enable when click new in angularjs

I have form with bootstrap modal, in that modal has a text box and add email button. If you type proper email and click on add email button then the button will be changed to remove email button and text box mode set to disable mode and another new text. but when you click on remove email button all text boxes will be changed to in enable mode.
see my code here
My html code
<!doctype html>
<html ng-app="app" ng-controller="ModalDemoCtrl">
<head>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" rel="stylesheet">
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.11.0.js"></script>
<script src="script.js"></script>
</head>
<div >
<script type="text/ng-template" id="myModalContent">
<div class="modal-header">
<h3 class="modal-title">I'm a modal!</h3>
</div>
<div class="modal-body">
<li ng-repeat="item in items " ng-form="subForm">
<input type="text" name="name" ng-model="item.email" ng-disabled="isDisable(disbl,$index)" required ng-pattern="/^[_a-z0-9]+(\.[_a-z0-9]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$/" ng-enter="addOrRemove($index,item.email)"/>
<span ng-show="subForm.name.$invalid || subFform.name.$pristine" style="color: red">Invalid email</span>
<button ng-disabled="subForm.name.$invalid || subFform.name.$dirty" ng-click="addOrRemove($index,item.email)" >{{item.value}}</button>
expression: {{subForm.name.$invalid}}
</li>
</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>
</script>
<button class="btn btn-default" ng-click="open('lg')" >Large modal</button>
</div>
</body>
</html>
My script code
var app = angular.module('app', ['ui.bootstrap']);
//var ModalDemoCtrl = function ($scope, $modal, $log) {
app.controller('ModalDemoCtrl',['$scope', '$modal','$log','$rootScope',
function controller($scope, $modal, $log, $rootScope)
{
$scope.open = function (size) {
$scope.val = "";
var modalInstance = $modal.open({
templateUrl: 'myModalContent',
controller: ModalInstanceCtrl,
size: size,
backdrop: 'static',
resolve: {
items: function () {
return $scope.items;
}
}
});
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
}]);
// Please note that $modalInstance represents a modal window (instance) dependency.
// It is not the same as the $modal service used above.
var ModalInstanceCtrl = function ($scope,$rootScope, $modalInstance) {
$scope.check2 = "hllo";
$scope.items = [
{
value: "Add email",
state: "1",
email: ""
}];
$scope.check1;
$scope.addOrRemove = function(indexSelected,rcvEmail)
{//alert($rootScope.email1);
if(!rcvEmail)
{
return
}
//console.log("just check email",rcvEmail);
//console.log("length of the object",$scope.items.length);
event.preventDefault();
if($scope.items[indexSelected].state == 1)
{
$scope.isDisable = function(value,index)
{
if(index <= indexSelected)
{
//console.log(index);
return value = "yes";
}
};
//console.log($scope.items[indexSelected].state);
$scope.items[indexSelected].value = "Remove email";
$scope.items[indexSelected].state = "0";
$scope.items[indexSelected].email = rcvEmail;
$scope.items.push({value: "Add email", state: "1"});
}
else
{
$scope.items.splice(indexSelected, 1);
$scope.isDisable = function(value,index)
{
console.log("indexes",index,"+",indexSelected);
/*index = index-1;
if(index <= $scope.items.length)
{
//console.log(index);
return value = "yes";
}
else
{
return value = "";
}*/
};
}
};
$scope.ok = function () {
$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
app.directive('ngEnter', function () {
console.log("directive checke");
return function (scope, element, attrs) {
element.bind("keydown keypress", function (event) {
if(event.which === 13) {
scope.$apply(function (){
scope.$eval(attrs.ngEnter);
});
event.preventDefault();
}
});
};
});
see my code here
Set your ng-disabled to be
(items[$index].state == 0) ? true:false
instead of a function.

How to show message for a certain time in angularjs

I'm displaying a message when the user clicks on the button.
I want to show this message for 10 seconds and then hide it.
My code is the following:
<script>
function Ctrl($scope, $window) {
$scope.greeting = 'Hello, World!';
$scope.doGreeting = function() {
$scope.msg="hi";
};
}
</script>
<div ng-controller="Ctrl">
<input type="text" ng-model="greeting" />
<button ng-click="doGreeting()">click</button>
{{msg}}
</div>
You can set a variable that determines whether to show the message or not and hide it and after 10,000 seconds. You will have to inject $timeout as shown below. Then in your view you will need to wrap {{msg}} in a span in order to use ng-show
<script>
function Ctrl($scope, $window, $timeout) {
$scope.greeting = 'Hello, World!';
$scope.showGreeting = false;
$scope.doGreeting = function() {
$scope.msg="hi";
$scope.showGreeting = true;
$timeout(function(){
$scope.showGreeting = false;
}, 10000);
};
}
</script>
<div ng-controller="Ctrl">
<input type="text" ng-model="greeting" />
<button ng-click="doGreeting()">click</button>
<span ng-show="showGreeting ">{{msg}}</span>
</div>
The way I did to show a message for a certain time in angularjs was using AngularJS-Toaster library
To use the library follow this steps:
<link href="https://cdnjs.cloudflare.com/ajax/libs/angularjs-toaster/0.4.9/toaster.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js" ></script>
<script src="https://code.angularjs.org/1.2.0/angular-animate.min.js" ></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angularjs-toaster/0.4.9/toaster.min.js"></script>
Add toaster container directive:
<toaster-container></toaster-container>
Prepare the call of toaster method:
// Display an info toast with no title
angular.module('main', ['toaster'])
.controller('myController', function($scope, toaster) {
$scope.pop = function(){
toaster.pop('success', "title", "text");
};
});
Call controller method on button click:
<div ng-controller="myController">
<button ng-click="pop()">Show a Toaster</button>
</div>
Here you can see a Plunker showing many kinds of toasts showing differents messages
Add the $timeout dependency to your controller. Here's a fiddle:
<div ng-app>
<div ng-controller="Ctrl">
<input type="text" ng-model="greeting" />
<button ng-click="doGreeting()">click</button>
<div class="ng-hide" ng-show="showMessage">{{msg}} {{ greeting }}</div>
</div>
</div>
function Ctrl($scope, $window, $timeout) {
var messageTimer = false,
displayDuration = 5000; // milliseconds (5000 ==> 5 seconds)
$scope.showMessage = false;
$scope.msg = "hi";
$scope.doGreeting = function () {
if (messageTimer) {
$timeout.cancel(messageTimer);
}
$scope.showMessage = true;
messageTimer = $timeout(function () {
$scope.showMessage = false;
}, displayDuration);
};
}

Resources