I have a problem with AngularJS Controller "as" syntax. I am trying to use bootstrap nanomodals and attach to them controllers $scope.
For now, I create angular component for my modal. I create my dialog in html too.
My html code:
<body ng-app="application">
<div ng-controller="accountController as $acc" class="controller-account">
<script type="text/ng-template" id="loginModal.html">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Nowe konto</h4>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label for="userNameInput">Nazwa użytkownika:</label>
<input type="text" class="form-control" id="userNameInput" placeholder="Wprowadź nazwę użytkownika">
</div>
<div class="form-group">
<label for="userPasswordInput">Hasło:</label>
<input type="password" class="form-control" id="userPasswordInput" placeholder="Password">
</div>
<div class="form-group">
<label for="userEmailInput">E-Mail:</label>
<input type="email" class="form-control" id="userEmailInput" placeholder="Wprowadź E-mail">
</div>
<button ng-click="$acc.okButton()" class="btn btn-primary">Załóż konto</button>
</form>
</div>
</script>
My JS script:
var angularApp = angular.module('application', ['ngAnimate', 'ngSanitize', 'ngCookies', 'ui.bootstrap']);
angularApp.component('loginModalComponent', {
templateUrl: 'loginModal.html',
bindings: {
resolve: '<',
close: '&',
dismiss: '&'
},
controller: () => {
var $acc = this;
$acc.$onInit = () => {
};
$acc.okButton = () => {
console.log('Liiiii');
//HTTP request about login
};
$acc.closeButton = () => {
$acc.close();
}
}
});
angularApp.controller('accountController', ['$scope', '$http', '$cookies', '$uibModal',
function($scope, $http, $cookies, $uibModal){
var $acc = this;
$acc.isUserSignIn = false;
$acc.loginModalOpen = () => {
var modalInstance = $uibModal.open({
animation: true,
component: 'loginModalComponent',
resolve: {
}
});
};
}]);
I dont know why, but i cannot use in this example $acc.okButton() function.
I used this and this to resolve this problem, but i cannot do this. Something is bad here, but i dont know what.
You declared a component, but I can not see it in your html code.
So firstly you need to take the html of the modal and place it to file 'loginModal.html' and remove 'ng-controller' directive from it. So the code in that file should be like this:
Seems that you need to replace this:
<div ng-controller="accountController as $acc" class="controller-account">
with this:
<login-modal-component class="controller-account" />
In such case it will use your controller described in component declaration.
But also you will add controllerAs: '$acc' to your component, because by default it uses $ctrl.
So you also need to slightly change code like this:
angularApp.component('loginModalComponent', {
templateUrl: 'loginModal.html',
bindings: {
resolve: '<',
close: '&',
dismiss: '&'
},
controller: () => {
var $acc = this;
$acc.$onInit = () => {
};
$acc.okButton = () => {
console.log('Liiiii');
};
$acc.closeButton = () => {
$acc.close();
}
},
controllerAs: '$acc' // ADD THIS LINE
});
Related
I am new to Angular. I have created a component with a template (panel.html) and a controller (allPanelsRetrieved). When a specific button defined in the template is clicked, the showDetails() function specified in the controller is called. This function triggers the opening of a modal dialog specified by a template (panel-list.details-template.html) and a controller, which are both defined inside the allPanelsRetrieved controller. The problem is that the modal dialog is displayed but the controller doesn't work (the click of the OK button does nothing). Where the problem may be? Thanks in advance
angular.
module('panelList')
.component('panelList', {
templateUrl: '/panel-list/panel.html',
controller: ['Panel', 'NgTableParams','$scope', '$location', '$uibModal',
function PanelListController(Panel, NgTableParams, $scope, $location, $uibModal) {
this.allPanelsRetrieved = (index, before) => {
//..hidden code here
}
this.showDetails = function () {
$uibModal.open({
templateUrl: '/panel-list/panel-list.details-template.html',
controller: function ($uibModalInstance) {
let $ctrl = this;
$ctrl.ok = function () {
$uibModalInstance.close();
};
},
}).result.then(
function (success) {
alert(success);
},
function (error) {
alert(error);
}
);
};
}]
});
Here is the template panel-list.details-template.html:
<div>
<script type="text/ng-template" id="/panel-list/panel-list.details-template.html">
<div class="modal-header">
<h3 class="modal-title">Modal title</h3>
</div>
<div class="modal-body">
Modal content
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="$ctrl.ok()">Close</button>
</div>
</script>
Use $scope to make it work in the AngularJS way. Here is a working plnkr demo of what you try to achieve.
$uibModal.open({
templateUrl: '/panel-list/panel-list.details-template.html',
controller: function ($uibModalInstance, $scope) {
$scope.ok = function () {
$uibModalInstance.close();
};
}
}).result.then(
function (success) {
alert(success);
},
function (error) {
alert(error);
}
);
View
<script type="text/ng-template" id="/panel-list/panel-list.details-template.html">
<div class="modal-header">
<h3 class="modal-title">Modal title</h3>
</div>
<div class="modal-body">
Modal content
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">Close</button>
</div>
</script>
While the angular.component implementation provides a default for the controllerAs syntax...
controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
...it appears that angular-ui-bootstrap does not.
Simply provide a value for controllerAs in your $uibModal options to continue using the preferred syntax.
$uibModal.open({
templateUrl: '/panel-list/panel-list.details-template.html',
controllerAs: '$ctrl',
controller: function($uibModalInstance) {
let $ctrl = this;
$ctrl.ok = function () {
$uibModalInstance.close();
}
}
http://plnkr.co/edit/q1arN18553XoUZEuPcPl?p=preview
The problem is with this keyword as you are using it. Use arrow function instead and it will work:
$uibModal.open({
templateUrl: '/panel-list/panel-list.details-template.html',
controller: ($uibModalInstance) => {
let $ctrl = this;
$ctrl.ok = function () {
$uibModalInstance.close();
};
},
})
I have the following code
$scope.currentTask = undefined;
$scope.openModal = function (task, size, parentSelector) {
var parentElem = parentSelector ?
angular.element($document[0].querySelector('.modal-demo ' + parentSelector)) : undefined;
var modalInstance = $uibModal.open({
animation: true,
ariaLabelledBy: 'modal-title',
ariaDescribedBy: 'modal-body',
templateUrl: 'myModalContent.html',
controller: 'homeController',
controllerAs: '$ctrl',
scope: $scope,
size: size,
appendTo: parentElem,
resolve: {
items: function () {
return $scope.items;
}
}
});
$rootScope.currentModal = modalInstance;
$rootScope.currentModal.result.then(function () {
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
}
$scope.setTask = function(task) {
$scope.currentTask = task;
}
$scope.log = function (currentTask) {
console.log($scope.currentTask);
}
<div class="row" ng-init="processPages()">
<div class="panel panel-default col-xs-3" style="margin: 20px" ng-repeat = "list in pages[currentPage]">
<div class="panel panel-default">
<div class="panel-heading">
<h6 style = "float: right">({{list.tasks.length}})</h6>
<div class="panel-title">
<h4>{{list.name}}</h4>
</div>
</div>
</div>
<div class="panel-body">
<table class="table table-striped table-hover" ng-init = "tasks = list.tasks">
<tr ng-repeat="task in tasks">
<td ng-click="setTask(task); openModal(task);>
<h5>{{task.name}}</h5>
</td>
</tr>
</table>
</div>
</div>
</div>
<script type="text/ng-template" id="myModalContent.html">
<div ng-init="editEnabled = false">
<div class="modal-header" ng-init = "log(currentTask)">
<h3 class="modal-title" id="modal-title" ng-show = "!editEnabled">Not editing</h3>
<h3 class="modal-title" id="modal-title" ng-show = "editEnabled">Editing</h3>
</div>
<div class="modal-body" id="modal-body">
<div ng-show="!editEnabled">
</div>
<div ng-show="editEnabled">
</div>
</div>
<div class="modal-footer">
<button style = "float: left" class="btn btn-primary" type="button" ng-show = "!editEnabled" ng-click="editEnabled = true">Editar</button>
<button class="btn btn-primary" type="button" ng-click="ok()">OK</button>
<button class="btn btn-warning" type="button" ng-click="cancel()">Cancel</button>
</div>
</div>
</script>
Here's how the page is working:
First we have a list of tasks and if we click on one task...
...we get a model
I'd like the task information to be passed to the model, but everytime I set the task on the $scope to be the task on the list, even though it happens before the model is opened the log function prints the currentTask is still undefined. I've tried to change the currentTask definition at the beggining to be something concrete, but what happens then is the log function prints what was defined at the beggining and not after the change is made.
The only solution I could find that worked was creating a different controller for the modal and sharing data between modalController and homeController via service. It's working fine, here's the service:
var app = angular.module('agendaApp');
app.service('sharedModalProperties', function() {
var task = undefined;
var activeModal = undefined;
return {
setCurrentTask : function (task) {
this.task = task;
},
getCurrentTask : function () {
return this.task;
},
setActiveModal : function (modal) {
this.modal = modal;
},
getActiveModal : function () {
return this.modal;
}
}
});
The task being undefined indicates the modal popup's ($scope) is not aware of any such model data. I assume its creating a fresh scope object, so your call to setTask() does not work as it is working on old scope. You can try calling during your modal display code.
$scope.openModal = function (task, size, parentSelector) {
var parentElem = parentSelector ?
angular.element($document[0].querySelector('.modal-demo ' + parentSelector)) : undefined;
var modalInstance = $uibModal.open({
animation: true,
ariaLabelledBy: 'modal-title',
ariaDescribedBy: 'modal-body',
templateUrl: 'myModalContent.html',
controller: 'homeController',
controllerAs: '$ctrl',
scope: $scope,
size: size,
appendTo: parentElem,
resolve: {
items: function () {
return $scope.items;
}
}
});
$rootScope.currentModal = modalInstance;
$rootScope.currentModal.result.then(function () {
}, function () {
$log.info('Modal dismissed at: ' + new Date());
}
//Add this code
$scope.currentTask = task;
//Or Call Set Task Here.
setTask(task);
);
}
From our discussion in the comments try something like this for your uibmodal's controller (See the git repo for the changes I made)
...
$scope.items = ["items1", "items2", "items3"];
var modalInstance = $uibModal.open({
animation: true,
ariaLabelledBy: 'modal-title',
ariaDescribedBy: 'modal-body',
templateUrl: 'myModalContent.html',
size: size,
appendTo: parentElem,
resolve: {
modalItems: function() {
console.log("items to send to modal", $scope.items);
return $scope.items;
}
},
controller: ['$scope', 'modalItems', function($scope, modalItems) {
console.log("items sent to modal", modalItems);
$scope.modalItems = modalItems;
}]
});
...
HTML:
<script type="text/ng-template" id="myModalContent.html">
{{modalItems}} <!-- Should show the array of items -->
</script>
Im having problems with the scope for a modal. Im using the Angular Bootstap library to show the modal. My code is:
angular.module('myApp.workspaces')
.controller('ModalInstanceCtrl', function ModalInstanceCtrl($scope) {
$scope.ok = function() {
console.log('in ModalInstanceCtrl and $scope.form is ', $scope.form);
};
})
.controller('WorkspacesCtrl', function WorkspacesController(workspacesService, $scope, $location, $modal) {
$scope.uploading = false;
$scope.noWorkspaces = false;
$scope.myWorkspaces = [];
$scope.showModal = function() {
$scope.opts = {
backdrop: true,
backdropClick: true,
dialogFade: false,
keyboard: true,
templateUrl: 'assets/workspaces/modalContent.html',
controller: 'ModalInstanceCtrl',
resolve: {}, // empty storage
scope: $scope
};
$modal.open($scope.opts);
};
});
And modalContent.html is:
<div class="modal-header">
<h1>Workspace Name</h1>
</div>
<div class="modal-body">
<form>
<fieldset>
<label for="name">Workspace Name:</label>
<input type="text" name="name" ng-model="form.name" />
</fieldset>
</form>
</div>
<div class="modal-footer">
<div ng-model="test">hjhhhjsad</div>
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
When I open the modal and then click OK,the console output is:
in ModalInstanceCtrl and $scope.form is undefined
I've tried every solution provided in a previous question here. Including:
passing scope: $scope as an option to the modal
changing ng-model="name" to ng-model="form.name"
changing ng-model="name" to ng-model="$parent.name"
However I still cannot get access to the inout value. What am I doing wrong?
EDIT
I realised I can access the input value with
$scope.$$childHead.$$childHead.form.name
This clearly doesnt seem right though....
It seems you are only missing the $scope variable.
.controller('ModalInstanceCtrl', function ModalInstanceCtrl($scope) {
$scope.form = {
name: "";
};
$scope.ok = function() {
console.log('in ModalInstanceCtrl and $scope.form is ', $scope.form);
};
})
$scope.form do not exists, try
ng-modal="formName"
I'm creating a webapp and I want to implement an option to add friends. I've created the add friend page as a modal with a text input field. I want to test this by displaying the input on my view page. How do I display this data onto my view page?
Here's what I currently have
index.html
<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>
<form name = "addFriendForm">
<input ng-model = "user.name"class="form-control" type = "text" placeholder="Username" title=" Username" />
{{ user.name }}
</form>
<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()">Add Friend</button>
<div> Username: {{user.name}}</div>
</div>
my JavaScript file:
angular.module('ui.bootstrap.demo', ['ui.bootstrap']);
angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function ($scope, $modal, $log) {
$scope.user = {name: ""}
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
resolve: {
items: function () {
return $scope.items;
}
}
});
modalInstance.result.then(function () {
$scope.user.name = user.name;}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
});
angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($scope, $modalInstance) {
$scope.ok = function () {
$modalInstance.close($scope.user.name);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
plunkr: http://plnkr.co/edit/JIoiNx47KXsY8aqbTUDS?p=preview
Resolve - plunkr
You could make use of modalInstance's resolve property; this acts as the link between the modal instance and the parent controller.
You inject the object in to the ModalInstanceController, and assign it to the scope of your modal instance.
UI Bootstraps resolve works exactly the same as ngRouter's; as such if for whatever reason resolve cannot resolve an object, the modal will not open.
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
resolve: {
user: function() {
return $scope.user;
}
}
});
Scope - plunkr
An alternative, and arguably simpler method would be to pass in the parents scope in to the modal. Note that currently this doesn't work when using controllerAs syntax on the parent controller.
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
scope: $scope
});
I would suggest using the promise from the result to set the value to your controller's variables instead. The "resolve" from my understanding is for passing controller's variable to the dialog for using inside the dialog, not to be changed directly. In your code example, passing result you set in $modalInstance.close() to modalInstance.result.then() will do the trick. In other words, changing line #18 and #19 to the following,
modalInstance.result.then(function (data) {
$scope.user.name = data;
To improve the code a bit, you can actually pass the entire user object to allow adding additional attributes such as email, address, etc. to your user object easily. I had create an example at http://plnkr.co/edit/oztYRNrCRlF1Pw289i1M?p=preview.
I am new to angular so apologies if I am missing a simple concept here. I have developed my application using a lot of directives to put in sections of my site. They have a template and a controller and I am assigning the controller inside of the direct. I intend to use these bits on other places on the site
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<ng-blah-logo></ng-blah-logo>
</div>
<div class="navbar-collapse collapse">
<ng-blah-login></ng-blah-login>
</div>
<!--/.navbar-collapse -->
</div>
</div>
<ng-blah-marketplace-form></ng-blah-marketplace-form>
The JS is (what I thought was) pretty straight forward:
var ngBlahMarketplaceFormCtrl = function($scope) {
console.log($scope)
$scope.form.name = ''
$scope.form.phone = ''
$scope.form.firm = ''
$scope.form.brief = ''
$scope.submit = function(){
console.log('ngBlahMarketPlaceFormCtrl::submit()')
console.log($scope.form)
}
}
app.directive('ngBlahMarketplaceForm', function () {
return {
restrict: 'E'
, transclude: true
, replace: true
, templateUrl: '/resources/ngViews/marketplaceForm.html'
, controller: 'ngBlahMarketplaceFormCtrl'
};
});
var ngBlahLoginCtrl = function($scope) {
console.log($scope)
$scope.default = {}
$scope.default.username = 'username' ;
$scope.default.password = 'password';
$scope.form = _.clone($scope.default) ;
$scope.submit = function() {
console.log('ngBlahLoginCtrl::submit()')
}
$scope.help = function() {
console.log('ngBlahLoginCtrl::help()')
}
}
// login form and the controller to logic
app.directive('ngBlahLogin', function () {
return {
restrict: 'E'
, transclude: true
, replace: true
, controller: 'ngBlahLoginCtrl'
, templateUrl: '/resources/ngViews/loginThin.html'
};
});
OK so the issue I have is that only one scope is being created. When I investigate the DOM the only element with a class="ng-scope" is the Body tag. What have I done to get this behavior? Do I need to post more code to assist in getting to the bottom of this?
Thank you in advance!
Edit: js fiddle: http://jsfiddle.net/U5UxX/
Inside of the JS Fiddle you can see two logs to the console inside of two different controller. Both times it logs the scope variable, and in both cases the scope.id are the same
Edit: clarifying the problem, which is that my two controllers share the same $scope variable. The page "renders" (the HTML) as expected. For some reason the only element with a class="ng-scpoe" attribute is the body tag (which contains the ng-app attribute as well).
Thanks again
This is working example based on your code.
added to directive scope:true
scope:true will create a child scope that will "prototypically" inherit from its parent, so you will be able to access values defined on parent's scope in your directive.
var fessmodule = angular.module('myModule', ['ui.bootstrap']);
fessmodule.controller('ngMarketplaceFormCtrl', function($scope) {
console.log($scope)
$scope.name = ''
$scope.phone = ''
$scope.firm = ''
$scope.brief = ''
$scope.submit = function () {
console.log('ngMarketPlaceFormCtrl::submit()')
console.log($scope.form)
}
});
fessmodule.directive('ngMarketplaceForm', function () {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: true,
controller: 'ngMarketplaceFormCtrl',
template: '<div class="well marketplaceWell">\
<div style="margin-left: 15px">\
<div class="form-group row">Start a RFP</div>\
<div class="form-group row"><input type="text" placeholder="name" class="col-md-9" ng-model="name"/></div>\
<div class="form-group row"><input type="text" placeholder="firm" class="col-md-9" ng-model="firm"/></div>\
<div class="form-group row"><input type="text" placeholder="phone number" class="col-md-9" ng-model="phone"/></div>\
<div class="form-group row"><textarea type="text" placeholder="proposal brief" class="col-md-9" ng-model="brief"></textarea>\
</div>\
<div class="form-group row">\
<button class="btn btn-default" ng-click="submit()">Submit</button>\
</div>\
</div>\
</div>'
};
});
fessmodule.controller('ngLoginCtrl', function($scope) {
console.log($scope)
$scope.
default = {}
$scope.
default.username = 'username';
$scope.
default.password = 'password';
$scope.form = _.clone($scope.
default);
$scope.submit = function () {
console.log('ngLoginCtrl::submit()')
}
$scope.help = function () {
console.log('ngLoginCtrl::help()')
}
});
// login form and the controller to logic
fessmodule.directive('ngLogin', function () {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: true,
controller: 'ngLoginCtrl',
template: '<div class="pull-left">\
<div class="login-form">\
<input type="text" id="username" ng-model="form.username" no-ng-dodilio-smart-default />\
<input type="password" id="password" ng-model="form.password" no-ng-dodilio-smart-default />\
<button class="btn btn-xs btn-primary" ng-click="submit()">SIGN IN</button>\
<button class="btn btn-xs btn-primary" ng-click="help()">?</button>\
</div >\
</div>'
};
});
console:
a {$id: "003", this: a, $$listeners: Object, $parent: e, $$childTail: null…}
a {$id: "004", this: a, $$listeners: Object, $parent: e, $$childTail: null…}
Fiddle
Hope it will help you,