ng-repeat not updating when coming back from a modal call - angularjs

I have the following code in my html:
<li class="col-md-4 my-show-hide-animation" ng-hide="sr.hidden" ng-repeat="sr in gmCtrl.gmSRs" ...
The above html page has a button that makes a modal call.
I have another html/js page that can be accessed modally and non-modally.
I have this after coming back from the modal call:
if (!$rootScope.$$listenerCount['sr.CreateDone']) {
$rootScope.$on('sr.CreateDone', function(event, data) {
ctrl.gmSRs.push(data.sr);
$uibModalStack.dismissAll();
});
}
Now... this actually works over and over again. However, the moment I go into the target html/js in a non-modal way (which also works), the modal way no longer works. In the debugger, I can see the data.sr object being added to the array, and I can see the array being changed, but it's not reflected in the view. Any ideas??? I assume that either I have some sort of scoping issue or its some weird angularjs bug.
Here's the call:
ctrl.modalInstance = $uibModal.open({templateUrl: "standardresponse/standardresponse.html",
controller: "StandardresponseController",
controllerAs: "standardresponseCtrl",
size: "lg",
keyboard: true,
backdrop: 'static',
windowClass: "app-modal-window",
});
I also added a line of code that shows I only have the 1 listener running.

If the array is changing, but it isn't showing up in the view, this means that the digest cycle isn't properly running on the $scope. You could fix this manually by calling $scope.apply() but you might want to reconsider your design.

Related

angular view not reflecting controller

This is an angular 1 app.
I have a directive that looks like this:
function addLiquidity() {
return {
restrict: 'E',
scope: {
expanded: '='
},
templateUrl: 'liquidity.html',
controller: 'addLiquidityCtrl'
};
}
And the html that looks like this:
<add-excess-liquidity expanded="addExpanded"></add-excess-liquidity>
In another html I have this:
expanded: {expanded}
This is false by default thanks to the controller that in its relevant part contains this:
$scope.addExpanded = false;
Now, when clicking a button to show form for adding new stuff, "addExpanded" is set to true.
When then the button to actually save the form is clicked "addExpanded" is set to false again:
$scope.addExpanded = false;
I can see in the controller that that function is called and that addExpanded behaves correctly in the controller.
Actually, it behaves correctly in the view too. So the value in html:
expanded: {expanded}
reflects the value in the controller.
However, as soon as I switch to another tab (this app is using nav-tabs) and come back to the tab I was before, then the value in html is not reflecting the controller anymore(which is still doing its job like before, going through the same steps as before, included setting the "expanded" to false).
I tried forcing view update with both timeout and "apply" but didn't help. Also, it seems somwehere else an apply is fired at the end of every function.
I really don't understand what's wrong here. Where should I look? Something happen (I guess) when leaving the tab the first time. But so many things happen so it is not easy to debug. Any clue anybody?
EDIT
I added a watch like this:
$scope.$watch( 'addExpanded',
function(newValue, oldValue){
console.log('addExpanded Changed');
console.log(newValue);
console.log(oldValue);
}
);
First time I go to the tab then it will print to console. I then leave the tab, then come back to it. And this time, when clicking on button, nothing is printed to the console.
Roughly, what happens when I change tab is that the div containing the tab-content is set to "display:none/block" (I guess) as it is pure css that is showing tab content upon selection.
In the main controller a few things are done depending on which tab was selected, but I cannot see things that would "destroy" the connection between the GUI and the controller/directive.
Also, each time I go to that tab, I see that the javascript connected to the directive is initiating. And the variable "expanded" is reset to false.
The issue is that, for some reason, the connection between javascript and the GUI is in some way "lost" when visiting the tab the second time (and for all the future until I refresh the page).

UI Router sticky onReactivate refresh listview

I'm using angular and ui-router with the extras so that I can use sticky state for maintaining listview scroll position on returning from the detail page in my mobile spa app.
My list is a kendo mobile listview. I'm implementing crud and would like to reflect the changes made in the listview when returning. I do not use the $state.go in my back buttons, but use $window.history.back because I want the back buttons to work "as expected", so I cannot use the reload parameter of that function.
I wired up the onReactivate and inject the service that has the flag to tell it whether crud operations have been done and to reload the list. This all works and I can successfully determine if I need to reload the screen.
So, I try to use the kendo.mobile.ui.ListView refresh method. I use angular.element("#idoflistview").data("kendoMobileListView").refresh(). It does find it and does refresh it, but all of the items are set to have a style="transform: translate3d(0px, 0px, 0px);", so they are all overlapping each other.
I originally did try to just do a $state.reload() and rebuild the page, and it did work in that it calls my init function in the controller and I could use the flag to know to request the data right way, but the listview would not build. The request for data was sent and retrieved, but the listview was empty.
I would prefer to just refresh the list if possible because the service has the kendo datasource with the updated data already, so there really is no need to make a call to the database again.
So, my setup for the listview works fine normally, but only breaks if I refresh in the onReactivate in that they are all there, but overlap each other because every li in the list starts at 0 (the update is in there though, so it did refresh).
I'm assuming that it is due to the fact that it needs to do it later. If so, how can I do that?
Any ideas on how to get the refresh method to work properly? Or is there a better way to do this?
Here is the abstract view:
<div ui-view="activityinquirylist" ng-show="$state.includes('activityinquiry.list')"></div>
<div ui-view="activityinquirydetails" ng-show="$state.includes('activityinquiry.details')"></div>
<div ui-view="activityinquirycrud" ng-show="$state.includes('activityinquiry.crud')"></div>
Here is how I'm declaring my listview:
<div id="activityListScroller" kendo-mobile-scroller="activityListScroller" k-pull-to-refresh="true" k-pull="vm.pullToRefresh" k-pull-offset="200" class="scroller-header-footer">
<ul id="activityListView" kendo-mobile-list-view="activityListView" k-data-source="vm.activityInquiryService.activityInqiuryDataSource" k-template="vm.activityTemplate" k-on-click="vm.listClicked(kendoEvent)" k-auto-bind="false" k-load-more="true"></ul>
</div>
Here is the code for the state:
.state('activityinquiry', {
//sticky: true,
abstract: true,
url: '/activityinquiry',
templateUrl: 'app/views/cm/activityinquiry.html'
})
.state('activityinquiry.list', {
url: '/activityinquirylist',
views: {
'activityinquirylist#activityinquiry': angularAMD.route({
templateUrl: 'app/views/cm/activityinquirylist.html',
controller: 'activityinquirylistController',
controllerUrl: 'controllers/cm/activityinquirylistController',
controllerAs: "vm"
})
},
sticky: true,
deepStateRedirect: false,
onReactivate: function (activityInquiryService) {
console.log("reactivating");
if (activityInquiryService.model.reloadList && activityInquiryService.model.reloadList == true) {
activityInquiryService.model.reloadList = false;
var item = angular.element("#activityListView").data("kendoMobileListView");
if (item) {
item.refresh();
}
}
}
})
EDIT:
After further investigation, I have found that it is due to the kendo.mobile.scroller. It is not available at the time that I am refreshing the listview, so it is somehow messing up how the listview does it's drawing. This makes no sense to me because the listview is inside the scroller (and still is if I inspect after).
If I remove the scroller, it works as expected, but I need that scroller because I am not using kendo views and have a footer on the page, so it will scroll with the content and I am using the pull to refresh functionality of the scroller (don't want to use the listviews pull to refresh because it does not allow me to handle how the refresh happens, while the scroller does).
Another Edit:
I have tracked it down to the fact that it only happens when I have loadMore true on the listview. If I turn this off, then it works fine.
So, in the code example above, if I set k-load-more="false" it works. I tried it with endless-scroll too and both have the same effect.

ui-router renders template before state changes

I have a nested state structure like this:
$stateProvider
.state('main', {
url: '',
abstract: true,
templateUrl: ...
controller: mainController,
resolve: {
Resolve1: ...
Resolve2: ...
}
})
.state('main.state1', {
url: '^/state1/:id/',
templateUrl: ....
controller: state1Controller,
resolve: {
Resolve11: ...
Resolve22: ...
},
})
.state('main.state2', {
....
From what I am seeing, when you are at state main.state1 and you navigate to the same state with another id parameter, the template for main.state1 is rendered fine but the template for main (the parent) is rendered before the state changes (I guess it doesn't wait for the data resolution of main.state1). This results to the view being rendered with the wrong data (more specifically, the state parameters that I use for generating links are wrong).
My mainController has the $stateParams and $state injected to it and that's where I get the data from.
Did anyone notice this before? Is this by design or is it a bug?
Is there any way to update the parent view with the latest data?
However, I would expect ui-router to wait for all data resolutions until it starts rendering the views (even the parent). I don't know if I am missing something here, but this is my understanding of the problem so far...
UPDATE:
I see now that a function that is involved in the view rendering (with interpolation) gets called many times, first with the old values and then with the new values. But the final result that I see on screen is using the initial data that were used when I first entered the 'main.*' state. This is SO weird.
UPDATE 2:
I have found that my links where not updated ONLY when using ui-sref (note that I was using parameters in the state href, e.g. ui-sref="main.state1({id:{{getId()}}})"). When switched to ng-href everything worked as expected.
I have no idea why, but that fixed the problem.
The odd thing was that the ui-sref was evaluated, but the link was not updated.
Go figure...
UPDATE 3:
This solution in turn caused other problems. Then the first click reloaded the application... So this is not the best fix...
The "solution" was to use ng-click with $state.go(), instead of links.
This appears to be a bug in ui-router.
ui-router transitions "through" the parent state to the child. That means main will be triggered, then main.state1. I ran into this myself where I was getting duplicate listeners and HTML DOM elements - main is actually running AT THE SAME TIME as main.state1. This behavior is by design, and AFAIK your only option is to design around it.

Opening angular-ui-bootstrap modal from javascript

I am trying to open the modal dialog from javascript when a certain condition is met. The example shown here http://angular-ui.github.io/bootstrap/ invokes the modal on ng-click.
How do I achieve showing modal when a certain condition is met?
Thanks
Additional Info
Sorry didn't mean to create confusion. I am going to clarify a little bit more by saying what I meant by "certain condition". The scenario is the user will search for customer by name and will get a list of customers back matching with the search string. The user then clicks on any one of the customer row to get more detail information.
When clicked the code control is handed off to Controller (It's an ASP.Net MVC app) which will then go through other classes and finally get data of the customer from database. It will then populate a boolean property called spouseNotFound. It then returns the JSON back to angularjs controller. Now assume that if this particular customer does not have spouse I want to show a modal saying that "Spouse not found".
So, no, I don't want the modal to be invoked on an event, rather than on business rule condition.
Hope that helps to understand things clearly.
Straight from the documentation.
$modal is a service to quickly create AngularJS-powered modal windows. Creating custom modals is straightforward: create a partial view, its controller and reference them when using the service.
Example plnkr. (http://plnkr.co/edit/?p=preview)
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl,
resolve: {
items: function () {
return $scope.items;
}
}
});
If anyone needs an easy approach to showing Bootstrap Modals with AngularJS, then you can simply use the following approach. It worked well for me:
var element = angular.element('#myModal');
// Open dialog
element.modal('show');
// Close dialog
element.modal('hide');
What you may be looking for is a $watch on your scope. This will allow you to monitor a variable, and when the value is what you want, then launch the modal as Nix describes.
$scope.$watch('entities', function(){
if($scope.entities[0].checked && $scope.entities[1].checked){
alert("If I were bootstrap, I'd be launching a modal right now!");
};
}, true);
A jsfiddle: http://jsfiddle.net/HB7LU/1636/

ngRepeat does not work inside AngularUI bootstrap dialog

does anyone know how to get my code working. I'm trying to make a user creation dialog and want to send post data using ngResource, I've already configured my PHP backend and its working as expected. Now, my problem is appending errors on the dialog, I've tried adding dummy data but ngRepeat does not work for me. I've also tried different steps debugging the problem but nothing works for me. I've also researching about the issue but no luck. here is my code:
<div name="errors" id="errors" class="alert alert-error">
<li ng-repeat="error in dialog.errors">{{ error }}</li>
</div>
try to look at the link
http://jsfiddle.net/QCQrF/
#Ryan there were number of things going on in your jsFiddle but the most important problem was the way you were specifying dialog's template. A template can be either specify as string (using the template property) or as a partial (templateUrl).
If you move a template to a partial you can specify it like follows:
var dialogOpts = {
backdrop: true,
keyboard: true,
backdropFade: true,
backdropClick: true,
templateUrl: 'login-dialog.html',
controller: 'DialogCtrl',
resolve: { dialogScope : {
errors : ['error1', 'error2'],
title :'Add User',
btnTxt : 'Create User'
}}
};
Also, IMO it is easier to pass data to be exposed on the dialog's scope using the resolve property instead of using dialog's instance.
I've converted your fiddle to a plunk to show this: http://plnkr.co/edit/iGIwPBj9zBPgX3IUwBNj?p=preview
You might find $dialog's service additional documentation helpful in further exploaration of this service: https://github.com/angular-ui/bootstrap/blob/master/src/dialog/README.md
As the last remark, I've noticed that you are were passing the $event argument to event handlers (all calling preventDefault() on them) while this is unnecessary.

Resources