ng-repeat inside of bootstrap modal does not get updated - javascript

I am using bootstrap modal dialog to handle chat logic view in my angularjs app.
I have a listener such this
$rootScope.$on('msg:received', handleNewMessageAction);
and handler for it
function handleNewMessageAction(e, data) {
var selTId = getSelectedThread().id;
if (data.message.threadId == selTId) {
data.message.getter = SessionService.getAuthProfile();
$scope.messages.push(data.message);
getSelectedThread().lastMessage.message = data.message.message;
} else {
for (var i = 0; i < $scope.threads.length; i++) {
if ($scope.threads[i].id == data.message.threadId) {
$scope.threads[i].lastMessage.message = data.message.message;
break;
}
}
}
$scope.$apply();
}
everything works well when I open dialog first time . But if I navigate to another sate (I am using ui-router) and then open modal again. I does not update view when handler pushed new item in array .I have debugged it more then once .Handler works fine it pushes new item in array (messages) but array in view does not get updated;
Here is the function which opens the dialog.
$scope.openModal = function () {
modalInstance = $modal.open({
templateUrl: 'views/partials/directives/modals/messages.html',
windowClass: 'extended-message-modal',
scope: $scope
});
return false;
};
and here is a small part of modal template
<div ng-repeat="msg in messages track by $index">
<div class="message-other-side pull-left" ng-if="msg.sender.id != authId">
<div class="pull-left message-profile-image">
<img ng-src="{{getImage(msg.sender.gender,msg.sender.avatar)}}">
</div>
<div class="message-content pull-left">{{msg.message}}</div>
</div>
<div class="message-my-side pull-right" ng-if="msg.sender.id == authId">
<div class="message-content pull-left">
{{msg.message}}
</div>
<div class="pull-left message-profile-image">
<img ng-src="{{getImage(msg.sender.gender,msg.sender.avatar)}}">
</div>
</div>
<div class="clearfix"></div>
</div>
What is the problem?

define a modal controller and use this controller scope to get and update the data inside modal.
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl'
});
};
If you're passing data to your modal from some other controller then you can use resolve to make it available in template scope.
$scope.open = function (dataFromOtherController) {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
resolve: {
data: function () {
return dataFromOtherController;
}
}
});
};
Now you can access $scope.data in modal controller and template.

Related

How to add some loading message in angularjs every route changes

Im new to angularjs and Im currently learning on angular's ng-route. I already done on it but what I want to achieve is to display first the loading message before displaying the content of the address of the route.
What's happening on my code right now is like this:
1. Click the home link
2. Display the loading message on the current page before successfully loading the content of the home link
That's how my code works, and I don't want it that way.
What I want to happen is something like this:
1. Click the home link
2. Current page will be hidden and display the loading message before displaying the content of the home link
I hope you understand. By the way here's my code:
index.html
<body ng-app="myApp" ng-controller="mainController">
<i class="fa fa-shield"></i> main
<i class="fa fa-home"></i> home
<!-- start views -->
<div ng-view></div>
</body>
script.js
var app = angular.module('myApp', ['ngRoute']);
app.config(function($routeProvider){
$routeProvider
.when('/',{
templateUrl: 'main.html',
controller: 'mainController',
resolve:{
delay: function($q, $timeout){
var delay = $q.defer();
$timeout(delay.resolve, 1000);
return delay.promise;
}
}
})
.when('/home',{
templateUrl: 'home.html',
controller: 'homeController',
resolve:{
delay: function($q, $timeout){
var delay = $q.defer();
$timeout(delay.resolve, 1000);
return delay.promise;
}
}
})
.otherwise({
redirectTo: '/'
});
});
app.controller('mainController', function($scope){
$scope.message = "...";
});
app.controller('homeController', function($scope){
$scope.message = "...";
});
app.directive('showDuringResolve', function($rootScope) {
return {
link: function(scope, element) {
element.addClass('ng-hide');
var unregister = $rootScope.$on('$routeChangeStart', function() {
element.removeClass('ng-hide');
});
$scope.$on('$destroy', unregister);
}
};
});
main.html
<h1>Main Page</h1>
<div show-during-resolve class="alert alert-info">
<strong>Loading.</strong>
Please hold.
</div>
home.html
<h1>Home Page</h1>
<div show-during-resolve class="alert alert-info">
<strong>Loading.</strong>
Please hold.
</div>
you can achieve the desired behavior with few modification in your code.
Add the loader text in your index.html file and show/hide the ng-view and loading text based on the rootScope variable statechange
<div ng-view ng-show="statechange"></div>
<div ng-show="!statechange" show-during-resolve class="alert alert-info">
<strong>Loading.</strong>
Please hold.</div>
in your directive make the following change.
app.directive('showDuringResolve', function($rootScope) {
return {
link: function(scope, element) {
element.addClass('ng-hide');
$rootScope.statechange = true;
var unregister = $rootScope.$on('$routeChangeStart', function() {
element.removeClass('ng-hide');
$rootScope.statechange = false;
});
scope.$on('$destroy', unregister);
}
};
});
Plunker : https://plnkr.co/edit/RISv2lvxwf75nuVfWLgA?p=preview
You can use a variable in each controller like dataReady, so for example in your home page, you can do something like:
html:
<div ng-show="dataReady">
<h1>Home Page</h1>
<div show-during-resolve class="alert alert-info">
<strong>Loading.</strong>
Please hold.
</div>
</div>
<div ng-show="!dataReady">
<h1> Loading ...</h1>
</div>
js:
app.controller('homeController', function($scope){
$scope.message = "...";
// After all the logic
$scope.dataReady = true;
});
And repeat the same idea for all the pages that you need.
By default set the loader to be hide
<strong class="loader" id="loader">Loading....</strong>
.loader{
display: none;
}
.loader-show{
display: inline-block;
}
When request made to get data, show the loader by adding loader-show class. On successful completion of request, hide loader by removing loader-show class.
That is,
resolve:{
delay: function($q, $timeout){
var loader = document.getElementById('loader');
loader.addClass('loader-show'); // when request is going to made
var delay = $q.defer();
$timeout(delay.resolve, 1000);
loader.removeClass('loader-show'); // when request ends
return delay.promise;
}
}

How do I pass the scope variables from a controller into the $ionicModal in ionic framework

I am building an app in Angular using ionic framework I am trying to pass the scope variable cropImgSrc to the modal's scope but it does'nt seem to work. Here is my code
Controller Code
angular.module('myApp').directive('addprofile',function(){
return{
restrict: 'E',
templateUrl: 'client/modules/add-profile-details/add-profile.html',
controllerAs: 'addProfileController',
controller: function($scope,$reactive,$ionicModal){
var vm = this;
$reactive(this).attach($scope);
this.helpers({
imgSrc: function(){
return '';
},
cropImgSrc: function(){
return '';
}
});
$ionicModal.fromTemplateUrl('client/modules/add-profile-details/crop-image-modal.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
vm.modal = modal;
});
this.addAvatar = function(files){
if (files.length > 0) {
var reader = new FileReader();
reader.onload = function(e){
$scope.$apply(function(){
vm.cropImgSrc = e.target.result;
vm.modal.show();
});
};
reader.readAsDataURL(files[0]);
}
else {
this.imgSrc = undefined;
}
};
}
} });
And my Template is as follows
<ion-view title="Crop Avatar">
<div class="modal">
<div class="bar bar-header bar-positive">
<h1 class="title">Your Avatar</h1>
</div>
<ion-content>
<img ng-src="addProfileController.cropImgSrc">
</ion-content>
</div>
Can anyone please guide me where I am going wrong?
First replace <img ng-src"addProfileController.cropImgSrc"> with <img ng-src="addProfileController.cropImgSrc">
Since the control within modal lies with $scope now, can you try changing this line vm.modal = modal; with $scope.modal= modal;
Also one option would be to print logs and check the values.
Please replace all 'vm' with $scope
and replace the modal code like this
<ion-view title="Crop Avatar">
<div class="modal">
<div class="bar bar-header bar-positive">
<h1 class="title">Your Avatar</h1>
</div>
<ion-content>
<img ng-src="{{cropImgSrc}}">
</ion-content>
</div>
</ion-view>

Getting controller name from $parent in AngularJS

I have converted one of my Angular controllers to Controller As syntax, but I am having trouble getting an ng-grid template to play nicely.
The controller has a function called edit user that looks like this
self.editUser = function (user_data) {
var modalInstance = $modal.open({
templateUrl: '/admin/views/adminuser.html',
controller: 'AdminUserController',
resolve: {
user_data: function () {
return user_data;
}
}
});
modalInstance.result.then(function () {
self.myQueryData.refresh = !self.myQueryData.refresh;
});
};
the ng-grid template looks like this
<div class="ngCellText" ng-class="col.colIndex()">
<a ng-click="$parent.$parent.$parent.$parent.editUser({user_id:row.entity.id, first_name:row.entity.first_name, last_name:row.entity.last_name, email:row.entity.email})">
<span ng-cell-text translate>Edit</span>
</a>
</div>
and my route looks like this
.when('/admin/settings', {
templateUrl: '/admin/views/settings.html',
controller: 'SettingsController as sc',
})
So the problem is in the template when I call
$parent.$parent.$parent.$parent.editUser
it doesn't know what I am talking about unless I include the controller name like
$parent.$parent.$parent.$parent.sc.editUser,
then it works great. However I don't want to bind this template directly to the sc controller. How can I call the editUser without using the controller name?
I was hoping there would be a function on the $parent that would supply the function name like
$parent.$parent.$parent.$parent.getController().editUser
Any suggestions?
You can call functions on parent scope directly without referring to $parent. Because you might get in to trouble later when you modify your view structure.
example:
<div ng-app="MyApp">
<div ng-controller="MyController">
{{myMessage}}
<div ng-controller="MyController2">
<div ng-controller="MyController3">
<div ng-controller="MyController4">
<button id="myButton" ng-click="setMessage('second')">Press</button>
</div>
</div>
</div>
<script>
angular.module('MyApp', [])
.controller('MyController', function($scope) {
$scope.myMessage = "First";
$scope.setMessage = function(msg) {
$scope.myMessage = msg;
};
}).controller('MyController2', function($scope) {
}).controller('MyController3', function($scope) {
}).controller('MyController4', function($scope) {
});
</script>
</div>
</div>
Or else you can use angular $broadcast
Since you are using controllerAs syntax, you can address your controller by the alias, so the actual template line will look like this:
<a ng-click="sc.editUser({user_id:row.entity.id, first_name:row.entity.first_name, last_name:row.entity.last_name, email:row.entity.email})">

Angular-UI Bootstrap : pass value from Trigger's button to Modal's button

Need some guidance. I am using angular-ui-bootstrap with php and mysql. I want to pass value from list of link (generated from php mysql) to modal button each time modal loads.
HTML
// Below link is while-loop with php-mysql result
Issue
<script type="text/ng-template" id="SubmissionReminder.html">
<div class="modal-header">
<h3 class="modal-title">Submission Work Order Request</h3>
</div>
<div class="modal-body">
Please ensure quotation(s) or any document(s) related to this Work Order is ready for Procurement Unit to proceed accordingly.
</div>
<div class="modal-footer">
<button class="btn btn-default" type="button" ng-click="cancel()">Cancel</button>
Submit
</div>
</script>
JS
app.controller('userWOController', function ($scope, $modal) {
$scope.animationsEnabled = true;
$scope.open = function () {
$scope.items = [];
var modalInstance = $modal.open({
animation: $scope.animationsEnabled,
templateUrl : 'SubmissionReminder.html',
controller: 'SubmissionReminder'
});
};
$scope.toggleAnimation = function () {
$scope.animationsEnabled = !$scope.animationsEnabled;
};
});
app.controller('SubmissionReminder', function ($scope, $modalInstance) {
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
I'm stuck on how to pass value from trigger (a href) to modal button (a href).
You can send data into modal controller pass through resolve object:
var modalInstance = $modal.open({
animation: $scope.animationsEnabled,
templateUrl : 'SubmissionReminder.html',
controller: 'SubmissionReminder',
resolve: {
refno: function () {
return {refno: $scope.refno};
}
}
});
Then get resolved refno from modal controller:
app.controller('SubmissionReminder', function ($scope, $modalInstance, refno) {
$scope.refno = refno().refno;
...
}

angular-ui bootstrap $modal service using directive instead

The examples I see of using angular-ui/bootstrap's $modal always look something like this:
$modal.open({
templateUrl: 'modaltemplate.html',
controller: function($scope) {
...
}
});
What if I want to use a directive, instead? Like this:
$modal.open({
template: '<my-modal-directive></my-modal-directive>'
// no "controller" property; use directive's controller
});
The markup for my-modal-directive renders fine, and I've moved the controller property into the my-modal-directive definition object, but now getting this error from the my-modal-directive:
Error: [$injector:unpr] Unknown provider: $modalInstanceProvider <- $modalInstance
Can anyone point me to an example where $modal uses a directive where that directive defines the controller?
For example, this works, where I've replaced the templateUrl with a directive:
http://plnkr.co/edit/YrGaF83GH6bzZPRR55GK?p=preview
But when I move the controller from $modal.open() into the directive, that's when the error happens:
http://plnkr.co/edit/aLBT239EpL004DRh4jll?p=preview
The problem is that $modalInstance can only be injected in the controller that you provide to $modal.open.
Check out the sources here:
$modal.open = function (modalOptions) {
...
var modalInstance = {
...
};
...
if (modalOptions.controller) {
...
ctrlLocals.$modalInstance = modalInstance;
...
ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
...
}
...
}
In essence when you try to add $modalInstance as a dependency to your controller AngularJS looks for a registered global provider named $modalInstanceProvider. Now the trouble is, if you understood the code above, that $modalInstance is not a globally registered provider. It only "exists" as a dependency for the controller you pass to $modal.open.
If you read the rest of the code you'll notice that $modal.open returns modalInstance, maybe you can use that.
Something like this:
function SomeController($modal) {
$scope.modal = {
instance: null
};
$scope.modal.instance = $modal.open({
template: '<my-modal-directive modal="modal"></my-modal-directive>',
scope: $scope
});
}
function MyModalDirective() {
scope: {
modal: '='
},
link: function($scope) {
// here you can access $scope.modal.instance
}
}
The issue you have is that you are trying to inject values which are not available for injection. Only values registered with the injector can be injected.
The logic of you code is also flawed, you are creating the modal in your main controller but trying to close it in the directive. Ideally, the modal should be triggered by the directive (via it's link function), and then you can ok/cancel it from there.
See my http://plnkr.co/edit/3p1rXAymd7BilyklgxKy?p=preview for one possible approach, I have kept the code that closes and cancels the modal in the main controller.
angular.module('ui.bootstrap.demo', ['ui.bootstrap']);
angular.module('ui.bootstrap.demo').directive('myModal', function() {
return {
restrict: 'E',
templateUrl: 'myModalContent.html',
controller: function ($scope) {
$scope.selected = {
item: $scope.items[0]
};
}
};
});
angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function ($scope, $modal, $log) {
$scope.items = ['item1', 'item2', 'item3'];
$scope.open = function (size) {
var modalInstance;
var modalScope = $scope.$new();
modalScope.ok = function () {
modalInstance.close(modalScope.selected);
};
modalScope.cancel = function () {
modalInstance.dismiss('cancel');
};
modalInstance = $modal.open({
template: '<my-modal></my-modal>',
size: size,
scope: modalScope
}
);
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
});
I create a directive to create modals easily. A modal content is based on a template view.
angular.module('your_app').directive('modalViewUrl', function ($modal) {
return {
restrict: 'A', // A: attribute
scope: { // isolate scope
'modalViewUrl': '#', // modal view url to render the modal content
'modalController': '#' // modal view controller (optional)
},
link: function($scope, element, attrs){
element.bind('click', function(){
var template =
'<div class="modal-body">' +
'<button ng-click="$close()" type="button" class="close" aria-label="Close">' +
'<span aria-hidden="true">×</span>' +
'</button>' +
'<div ng-include="\'' + $scope.modalViewUrl + '\'"></div>' +
'</div>';
// see modal reference from ui bootstrap at <http://angular-ui.github.io>
var modalInstance = $modal.open({
animation: true,
template: template,
controller: $scope.modalController,
});
});
}
};
});
Example how to use it:
index.html
<a modal-view-url="hello.html" modal-controller="HelloCtrl" href="#">
Click here to open the modal
</a>
hello.html
<h1> Hello World {{name}} </h1>
HelloCtrl.js
angular.module('yourApp').controller('HelloCtrl',
function ($scope, $modalInstance) {
// $modalInstance: same from from ui bootstrap
$scope.name = "Xico";
});
A modal view can have its own controller. Example:
hello.html (modified)
<h1 ng-controller="Hello2Ctrl"> {{msg}} {{name}} </h1>
Hello2Ctrl.js
angular.module('yourApp').controller('Hello2Ctrl',
function ($scope) {
$scope.msg = "Hello Worldsszz";
$scope.name = "Zefa";
});
Observe that the modal output will be "Hello Worldsszz Xico", because the modal controller (HelloCtrl) will be rendered after view controller (Hello2).
Reference
It's even more late reply, but someone may find it useful.
I have enhanced Fernando Felix answer and made my own quite flexible directive which communicates with the controller, which I think might be solution for this question.
Directive
var modalUrl = function ($modal) {
return {
restrict: 'A', // A: attribute
scope: { // isolate scope
'modalUrl': '#', // modal view url to render the modal content
'modalController': '#', // modal view controller (optional)
'value': "="
},
link: function(scope, element, attrs){
console.log('modalUrl link');
var modalInstance;
var template = [
'<div class="modal-body">',
'<button ng-click="$close()" type="button" class="close" aria-label="Close">',
'<span aria-hidden="true">×</span>',
'</button>',
'<div ng-include="\'' + scope.modalUrl + '\'"></div>',
'</div>'
].join('');
element.bind('click', function(){
// see modal reference from ui bootstrap at <http://angular-ui.github.io>
modalInstance = $modal.open({
size: attrs.size,
animation: true,
template: template,
resolve: {
params: function () {
console.log('value passed to modal:');
console.log(scope.value);
return scope.value;
}
},
controller: scope.modalController
});
modalInstance.result.then(
function (returnValue) {
// alert('value: '+returnValue);
console.log('modal returnValue:');
console.log(returnValue);
scope.value = returnValue;
}, function () {
console.log('Modal dismissed at: ' + new Date());
}
);
});
}
};
}
modalUrl.$inject = ['$modal'];
angular.module('app').directive('modalUrl', modalUrl);
Controller
var HelloCtrl = function ($scope, $modalInstance, modalVal) {
// $modalInstance: same from from ui bootstrap
console.log('Hello init!');
// modalVal is the init modal value passed via directive
console.log(modalVal);
// your code
$scope.name = modalVal;
$scope.ok = function() {
$modalInstance.close(this.name); // returnValue
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
}
HelloCtrl.$inject = ['$scope', '$modalInstance','params'];
angular.module('app').controller('HelloCtrl',HelloCtrl);
inline template
<script type="text/ng-template" id="hello.html">
<div class="modal-header">
<h3 class="modal-title">I'm a modal!</h3>
</div>
<div class="modal-body">
<input type="text" ng-model="name" />
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn" ng-click="cancel()">Cancel</button>
</div>
</script>
It's one controller and template per popup type, then you can call it multiple times with:
<a modal-url="hello.html" modal-controller="HelloCtrl" value="yourVal" ng-init="yourVal='test'" href="#">Click here to open the modal</a>
You can initialize value with whatever - ie. object, array etc.
or external template
Pretty much the same, just url changes and template file is used for template.
<a modal-url="/modal/test1.html" modal-controller="HelloCtrl" value="yourVal" ng-init="yourVal='test'" href="#">Click here to open the modal</a>
test1.html
<div class="modal-header">
<h3 class="modal-title">I'm a modal!</h3>
</div>
<div class="modal-body">
<input type="text" ng-model="name" />
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn" ng-click="cancel()">Cancel</button>
</div>
Modal size etc.
Just add parameter size="sm|lg" for the modal link/button ie.
Click here to open the modal
For standard size skip the parameter.
You may enhance it yourself using link function attrs.
I'm kanda late replay put simplest way is to use
$scope.$parent.$close(result);
$scope.$parent.$dismiss(reason);
This works form your directive controller.

Categories

Resources