I am currently using AngularStrap v2.3.1 and AngularJS v1.4.5.
I've been having some trouble getting AngularStrap's modals to work. It's most likely a misunderstanding of how it should work, as the documentation isn't too helpful. I am using the controllerAs syntax everywhere in my app.
From what I understood, I could create an object in the controller as such:
vm.editModal = {
contentTemplate: '/templates/edit.html',
backdrop: 'static',
show: false
};
And then in my view, I would use the bs-modal with the modal property:
<a href="" bs-modal="ctrl.editModal">
However, all I get is a blank modal. It actually doesn't seem to be taking any of the properties from editModal. So it led me to believe that doing it that way only uses the title and content attributes. So it's for a basic modal?
I also tried using the $modal service, which I did inject into the controller. And then tried:
vm.editModal = $modal({
show: false,
contentTemplate: '/templates/edit.html',
backdrop: 'static',
});
And I used the same HTML:
<a href="" bs-modal="ctrl.editModal">
However, in this case, I got a bunch of errors in the console and it didn't work either. Some errors;
[ng:cpws] Can't copy! Making copies of Window or Scope instances is not supported.
[$rootScope:infdig] 10 $digest() iterations reached.
As soon as I can get this working, I'd then like to try resolving data before the modal loads. But I haven't even gotten that far yet.
If someone could point me in the right direction, it'd be much appreciated.
Thanks!
mgcrea has a great example using a service to exchange information with the modal
.service('$confirm', function($modal, $rootScope, $q) {
var scope = $rootScope.$new();
var deferred;
scope.title = 'confirm';
scope.content = 'Confirm deletion?';
scope.answer = function(res) {
deferred.resolve(res);
confirm.hide();
}
var confirm = $modal({template: 'confirm.tpl.html', scope: scope, show: false});
var parentShow = confirm.show;
confirm.show = function() {
deferred = $q.defer();
parentShow();
return deferred.promise;
}
return confirm;
})
.controller('ModalDemoCtrl', function($scope, $confirm) {
$scope.delete = function() {
$confirm.show().then(function(res) {
console.warn('answered', res)
})
};
Here -> http://plnkr.co/edit/KnRMGw5Avz2MW3TnzuVT
Hope this helps
Related
I am trying to integrate Angular bootstrap modal form,
Here is my controller
angular.module('app').controller('groupController',['groupService','$uibModal','toastr','$scope',function(groupService,toastr,$uibModal,$scope)
I have added $uibModal as dependency.In my app.js i have added ui.bootstrap as dependency too,
My angular.bootstrap version is 1.3.3
my modal function is as follows
vm.viewGroupDetail = function(userDetails) {
var scope = $scope.$new();
scope.userDetails = userDetails;
vm.modalInstance = $uibModal.open({
animation: true,
templateUrl: 'app/views/groups/group_details_modal.html',
windowClass: 'd-modal',
size: 'md',
scope: scope
// resolve: {
// userDetails: function () {
// return ;
// }
// }
});
};
When i try using breakpoints and check the control flow, It goes inside viewGroupDetail function. but at the point of $uibModal.open() , the control breaks
What am i missing here?
I tried other related questions in stack overflow, nothing gave me a solution, so posting my own Query
You are posting $uibModal in the wrong place in the parameter declaration
'groupService','$uibModal','toastr','$scope',function(groupService,toastr,$uibModal,$scope)
Order must be maintained according to inject.
Try like this
'groupService','$uibModal','toastr','$scope',function(groupService,$uibModal,toastr,$scope)
I have small problem to solve.
I have modal controller rejectIssueModalCtrl.js
(function () {
'use strict';
function rejectIssueModalCtrl($modalInstance, issue, $rootScope) {
var self = this;
self.cancel = function () {
$modalInstance.dismiss('cancel');
};
self.reject = function ($rootScope) {
$modalInstance.close(self.reason);
console.log(self.reason);
};
$rootScope.reasono = self.reason;
}
rejectIssueModalCtrl.$inject = ['$modalInstance', 'issue', '$rootScope'];
angular
.module('app')
.controller('rejectIssueModalCtrl', rejectIssueModalCtrl);
})();
When I click the button I can open this modal and write a reason. I want to show this reject reason in table in other controller.
Here's my code from other controller issueDetailsCtrl.js
$scope.reasonoo = $rootScope.reasono;
function rejectIssue() {
var rejectModal = $modal.open({
templateUrl: '/App/Issue/rejectIssueModal',
controller: 'rejectIssueModalCtrl',
controllerAs: 'rejectModal',
size: 'lg',
resolve: {
issue: self.issueData
}
});
rejectModal.result.then(function (reason) {
issueSvc
.rejectIssue(self.issueData.id, reason)
.then(function (issueStatus) {
changeIssueStatus(issueStatus.code);
getIssue();
}, requestFailed);
});
};
and html code
<div>
<span class="right" ng-bind="$root.reasono"></span>
</div>
As you can see I tried to use $rootScope. I can console.log the reason but I cant make it to show in this html. Any help?
We're missing some context, but I believe this is your problem:
self.reject = function ($rootScope) {
$modalInstance.close(self.reason);
console.log(self.reason);
};
$rootScope.reasono = self.reason;
Assuming self.reason is bound to the input in your modal, it won't be defined outside of the reject callback - that's the nature of async. You're able to log to console because you're doing so within the callback.
Define $rootScope.reasono inside of the callback like so:
self.reject = function () {
$modalInstance.close(self.reason);
console.log(self.reason);
$rootScope.reasono = self.reason;
};
Edited to show that $rootScope should be removed as a named parameter in the reject function definition.
Using root scope is not recommended. For this reason it is recommended to create a service for intercommuncation with variable to store reject reason, then inject this service for each controller - that way you will be able to read/write reason from different controllers.
I've been with Angularjs a few days and I'm struggling with a few aspects of it. I'll do my best to try and explain what the issue is, and I'd really appreciate any help anyone can give me about it.
My situation (simplified) is this:
I have a service which loads some info from a json and stores it in an object. It also have some functions to be used for other controllers to retrieve that information.
var particServices = angular.module('particServices', []);
particServices.service('particSrv', function() {
var data = {};
this.updateData = function(scope) {
data = // http call, saves in data
}
this.getName = function(code) {
return data.name;
}
});
I have an html page assisted by a controller, which uses a directive board (no params, really simple). This is the controller:
var bControllers = angular.module('bControllers', []);
bControllers.controller('bController', ['$scope', 'particSrv', function ($scope, particSrv) {
$scope.getName = function(code) {
return particSrv.getName(code);
};
particSrv.updateData($scope);
}]);
As you can see, the controller makes the call to initialize the object in the service. As this is a singleton, I understand once that info is loaded no other call needs to be make to updateData and that info is available to others using the getters in the service (getName in this case).
I have a really simple directive board (which I simplified here), which uses another directive bio.
angular.module('tsDirectives', [])
.directive('board', ['dataSrv', 'particSrv', function(dataSrv, particSrv) {
return {
restrict: 'E',
replace: true,
scope: true,
controller: function($scope) {
$scope.getName = function(code) {
return particSrv.getName(code);
};
dataSrv.updateData($scope, 'board', 'U');
},
templateUrl: '<div class="board"><div bio class="name" partic="getName(code)"/></div></div>'
};
}]);
And this is the bio directive:
angular.module('gDirectives', [])
.directive('bio', function() {
return {
scope: {
partic: '&'
},
controller: function($scope) {
$scope.name = $scope.partic({code: $scope.athid});
},
template: '<a ng-href="PROFILE.html">{{name}}</a>'
};
})
Now, what I expected is that in the bio directive the info retrieved from party was displayed, but apparently this directive is processed before the partic is initialized in the main controller.
I was under the impression that even though this information was still not loaded when the directive is processed, as soon as the service finishes and the info is ready, automagically it would appear in my directive, but that does not seem to work like that. I've been reading about $watch and $digest, but I fail to see why (and if) I would need to call them manually to fix this.
Any hint will be much appreciated. I could provide more technical details if needed.
Directive will initialise when app is loaded and user opens the page where that directive is, if you have some property that is set later (from api for example), it will update that property in directive but that directive will not be reinitialised ($scope.partic({code: $scope.athid}) wont be called).
If you want for directive to wait for initialisation you should use ng-if. Something like this:
<div data-directive-name data-some-property="someProperty" data-ng-if="someProperty"></div>
In this case directive will be initialised when (if) you have some value in $scope.someProperty. But this is not very good if you can have false values for someProperty.
In that case you would need to use some kind of loaded flag.
You have not included "particServices" as a dependency in other modules which use the services of "particServices". Your modules should look like:
var bControllers = angular.module('bControllers', ['particServices']);
angular.module('tsDirectives', ['particServices']);
angular.module('gDirectives', ['particServices']);
I'm trying to figure out where exactly the controller object gets published, when using the "controller as" syntax and instantiating the controller with $compile. Here's a test I have:
describe('"Controller as" syntax', function() {
it('should work when controller is instantiated via $compile', function() {
var $injector = angular.injector(['ng', function($controllerProvider) {
$controllerProvider.register('DummyController', function() {
this.message = 'hello there';
});
}]);
var $compile = $injector.get('$compile');
var $rootScope = $injector.get('$rootScope');
var $scope = $rootScope.$new();
var view = $compile(
'<div ng-controller="DummyController as dummy">{{dummy.message}}</div>'
)($scope);
$rootScope.$apply();
// OK - The view is updated
expect(view.text()).toBe('hello there');
// FAIL - 'dummy' is not available on the $scope
expect($scope.dummy.message).toBe('hello there');
});
});
Please see the very last expectation. I expect dummy to be available via $scope, but it doesn't seem to be there. Am I wrong and it's not supposed to work like this?
I've also discovered that it's available via $scope.$$childTail and $scope.$$childHead:
// OK - 'dummy' is available here
expect($scope.$$childTail.dummy.message).toBe('hello there');
// OK - 'dummy' is available here
expect($scope.$$childHead.dummy.message).toBe('hello there');
Does it mean that there's a child scope created in my case, and dummy gets only published on that child scope?
From $compile docs:
scope
If set to true, then a new scope will be created for this directive.
From ngController.js:
var ngControllerDirective = [function() {
return {
restrict: 'A',
scope: true,
controller: '#',
priority: 500
};
}];
So, it's as expected, my expectation was wrong. The only way to get to that dummy property is via those $$... things, which are not a part of public API.
I've spent the night on trying to figure this out and have finally decided to give up and ask for help.
I'm building a web-app with AngularJS that is designed to work with flakey connections (mobiles).
I'm trying to implement the functionality for a user to add a object (whether that's an appointment, book, etc is irrelevant) to the server.
Service that handles syncing objects with the server:
angular.module('App')
.service('syncUp', function syncUp($http, $q, app) {
this.addObject = function addObject(object) {
var deferred = $q.defer();
app.inSync = false;
var httpConfig = {
method: 'POST',
url: 'http://myurl.dev/app_dev.php/api/add-object',
data: object
}
function persist() { setTimeout(function() {
$http(httpConfig).
success(function(data, status) {
app.inSync = true;
deferred.resolve(data.id);
}).
error(function(data, status) {
app.inSync = false;
persist();
});
}, 3000);
};
persist();
return deferred.promise;
}
});
'app' service that the status bar is bound to:
'use strict';
angular.module('App')
.service('app', function app($http, $q) {
this.inSync = true;
});
Template binding to the 'app' service inSync property:
<div class="status" ng-class="{'insync':inSync}"></div>
Specific object service that sends data from the controller to the syncUp service:
this.addBook = function(book)
{
var tempId = syncUp.generateUid();
this.books[tempId] = book;
this.books[tempId].tempId = tempId;
syncUp.addObject({
'type': 'book',
'data': this.books[tempId]
}).then(function(newId) {
booksRef[newId] = book;
delete booksRef[tempId];
}, function() {});
}
Everything is working as it should (data is being persisted to the server and the ID is being returned and replacing the tempId just fine. The problem is, when the inSync key on the 'app' service is updated, the class isn't added/removed from the div as it should be with ng-class in the template. If I load another route, that will force iterate through whatever internal cycle angular is doing and update the class on the template.
I've tried all manner of $apply() solutions, moving where the app.inSync key is set back to true, looping a function watching it. It's being set in all the right places (from debugging I know it's set back to true correctly), I just can't figure out how to make the change appear on the UI.
I tried:
$rootScope.$apply(function() {
app.inSync = true;
});
Which gave me an error (already running a digest, or something).
So I tried the 'safeApply' version that has been circulated on many answers/blogs, which didn't throw the error, but didn't work either.
As far as I can figure out, the UI should be updated when promises are resolved (both the http and my syncUp.addObject promise are resolved, so I'm not sure why it's not working.
Any ideas? I need to keep the current implementation of promises to be able to set the returned ID from the server on the added object, to avoid a circular-dependency issue between the syncUp and object angular services.
Edit:
And the status bar directive:
angular.module('App')
.directive('navigation', function (app) {
return {
templateUrl: '/app/views/navigation.html',
restrict: 'E',
link: function (scope, element, attrs) {
scope.inSync = app.inSync;
}
}
});
References you make in templates refer to objects on the current $scope. Services do usually not create or add anything to the $scope, so putting properties on a service, will not make them available to the template. To get stuff on the $scope, you need to use a controller. You can use the ng-controller directive to reference a controller, you'll find examples of this in the first AngularJS tutorials.
What you should do is create a controller and have it listen for events from the service. Here's an example of how to do that.
That's the nice way; You might also be able to get away with it by putting the inSync = true on the $rootScope as such;
service('syncUp', function syncUp($http, $q, app, $rootScope) {
// (...)
$rootScope.inSync = true;
It looks like you're hoping to see bindings operating between a service ('app') and a template. It's hard to tell if we're not seeing the entire picture. Going on that assumption, you need to refactor so that you are setting up bindings on a controller.
I would expect the controller setup to look something like this:
angular.module('App')
.controller('app', function app($http, $q, $scope) {
$scope.inSync = true;
});
Now you will have two-way binding hooked-up on the 'inSync' property.
Otherwise, your template looks fine.
If I'm off base, please update your question with more context, or better yet make a fiddle to boil down the problem.