Short version of my question is: How do I change the URL without need to trigger route change or without need to run all the controllers on the currently displayed page?
Details:
I have a template which is displayed inside the <ng-view> that has regions governed by 3 controllers. On the very top of the page I have an interactive map. When you click on the regions it broadcasts a click and other component picks up on it and displays data about this region. Really simple setup.
What I would like to do is allow my users to deep link to the content. So every time someone clicks on a link I'd like to change the URL that can be copied and pasted to another browser. Some other user could just click the link and see the same state the first one saw.
Currently I change the location with code similar to this one:
$scope.$on('mapRegionClick', function($scope, regionCode) {
var url = generateURL(regionCode);
$scope.currentScope.$apply(function(){
$location.path(url);
});});
The URL is then picked up in my routing and the map plus data displays correctly. The downside of this is that every time I click on the map and URL changes the whole template / view is regenerated. Because generating the map is kind of heavy I'd like to trigger only a change to the data presenting controller.
Is it possible? How?
I could do some communication between controllers and achieve the my goal but then I would not be able to do deep linking.
PS: I do not want to use $location.search() and reloadOnSearch=false. my links have to be pretty :)
Sounds like you don't want to use $route service.
The $route service is designed to reload the controllers so that there is no difference between navigating to a URL and refreshing the URL. We do this by doing a full reload on every URL change. This is intentional.
Sounds like your use case, should not be using $route, just $location and ng-include.
You can use $locationChangeStart event to store the previous value in $rootScope or in a service. When you come back, just initialize all the previously stored values from the $rootScope. Check this quick demo using $rootScope.
var app = angular.module("myApp", ["ngRoute"]);
app.controller("tab1Ctrl", function($scope, $rootScope) {
if ($rootScope.savedScopes) {
for (key in $rootScope.savedScopes) {
$scope[key] = $rootScope.savedScopes[key];
}
}
$scope.$on('$locationChangeStart', function(event, next, current) {
$rootScope.savedScopes = {
name: $scope.name,
age: $scope.age
};
});
});
app.controller("tab2Ctrl", function($scope) {
$scope.language = "English";
});
app.config(function($routeProvider) {
$routeProvider
.when("/", {
template: "<h2>Tab1 content</h2>Name: <input ng-model='name'/><br/><br/>Age: <input type='number' ng-model='age' /><h4 style='color: red'>Fill the details and click on Tab2</h4>",
controller: "tab1Ctrl"
})
.when("/tab2", {
template: "<h2>Tab2 content</h2> My language: {{language}}<h4 style='color: red'>Now go back to Tab1</h4>",
controller: "tab2Ctrl"
});
});
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular-route.js"></script>
<body ng-app="myApp">
Tab1
Tab2
<div ng-view></div>
</body>
</html>
Related
I have a simple modal that appears when the user enters a page, but it is creating two of them. I have looked through the app and there are not two calls to the modal.
Here is the controller for it:
angular.module('rokoApp')
.controller('FinanceCtrl', function($scope, $modal) {
$modal.open({
templateUrl: 'includes/modal.html',
controller: function ModalInstanceCtrl($scope, $modal,$modalInstance){
console.log('opened')
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}
});
will be much better if you give us an example of functional code in plunker or jsfiddle, because the problem could be in another place.
For example:
When we started creating an app with AngularJS usually we put ng-controller in a upper tag and write the first lines of our app. The problem is that some times when we added routing we forget remove the ng-controller and then the controller is executing twice. The first time with the configuration of the module and again with the processing of the html.
So the solution here is to remove the tag in the html if we use routing or delete the controller property of routing and keep the ng-controller in the html.
I will let you the two codes:
With the error: http://plnkr.co/edit/TmQ5QhjD55WTurQNaEIv
Without the error: http://plnkr.co/edit/1xGUGEvAZ6jUBs8CbhTT
My Question is can we reload the view in ui-router if you are on same state.
check my code at`http://plnkr.co/edit/MA7CuyH2RFrlaoAgBYog?p=preview
My app.js file is
var app = angular.module('plunker', ['ui.router']);
app.config(function($stateProvider) {
$stateProvider
.state("view1", {
url: "/view1",
templateUrl: "x.html"
})
.state("view2", {
url: "/view2",
templateUrl: "y.html"
})
})
app.controller("MainCtrl",function(){});
And index page is
<body ng-controller="MainCtrl">
Accounts
Dashboard
<div ui-view></div>
Now click on Dashboard link here you will see a text box. fill any value in that. Now again click on Dashboard link. now the state should reload and all data of page should be reloaded including its controller. Please make sure to ui-router only.
Thanks
add attribute to the element with
ui-sref=""ui-sref-opts="{reload:true}"
example:
<a ui-sref-opts="{reload:true}" ui-sref="app.post.applicants">Applicants</a>
Use ng-click and write a controller function with $state.go
You could catch the click in a "ng-click" and use the $state service to transition/reload pragmatically
I had this similar issue and I solved this in two simple way ...
First I wrote a function into related controller (my state was 'inbox' you can use your own ui-state):
$scope.reload = function() {
$state.go('inbox', null, { reload: true });
}
Second call this function to anchor tag:
<a ng-click="reload()">Inbox</a>
Hope it will help. :)
I'm trying to figure out how to show or hide an element in my navbar based on the route, or the current view being displayed. For example, I have an basic/advanced toggle button that I put in the navbar (Bootstrap 3) when the user is on the search form. But when they are anywhere else in the app that toggle button should be hidden.
In terms of the DOM, it's just a list item that builds out the nav. I'm not sure if I should show or hide based on a global value that gets set on each view, or if I can just use the route or view name. If so how that would work?
Thanks!
One solution is to build a function in the controller that is responsible for the navbar that could be queried to determine if the element should be displayed:
$scope.isActive = function(viewLocation) {
return viewLocation === $location.path();
};
(The above code uses Angular's $location service)
Then within the template, you can show/hide based on the result of the call (passing in the route that should toggle displaying the element):
ng-show="isActive('/search-form')"
Here's the approach I took with ui-router:
I only want to hide the navbar for a small number of pages so I went with an opt out property on the state(s) that I want to hide the navbar.
.state('photos.show', {
url: '/{photoId}',
views: {
"#" : {
templateUrl: 'app/photos/show/index.html',
controller: 'PhotoController'
}
},
hideNavbar: true
})
Inject $state in your navbar's controller and expose it to the template:
$scope.state = $state;
Then add ng-hide to your navbar template:
<nav ng-hide="state.$current.hideNavbar" ...
Above works perfectly using ui-router don't forget to pass $scope and $state within your function
Example:
app.controller('LoginCtrl', function($scope, $state){
$scope.state = $state;
console.log($state); // this will return the current state object just in case you need to see whats going on for newbies like me :)
});
I've just started using Onsen UI by implementing the bootstrap example and I've been trying to get the view to update when I change the page attribute.
<body ng-controller="PageLoadingCtrl">
<ons-screen page="{{loadedPage}}"></ons-screen>
</body>
My controller's code:
app.controller('PageLoadingCtrl', ['$scope', '$timeout', 'notificationService',
function($scope, $timeout, sharedService){
$scope.loadedPage = "index.html";
$scope.updatePage = function(page){
$timeout( function (){
$scope.loadedPage = page;
$scope.$apply();
}, 500);
};
$scope.$on('changePage', function (event, message){
$scope.updatePage(message);
});
}
]);
As you can see I'm using a controller on the body object so that I can update the loadedPage variable however, when I fire the changePage event, the view doesn't change.
After checking the DOM elements with web inspector I can see that page attribute is equal to whatever I pass to the updatePage function.
So far I tried to force a refresh with $apply and $digest but that still doesn't do the trick.
Cheers!
Because ons-screen need to maintain page stack. it's not intuitive to use binding for the page attribute.
Using ons.screen.presentPage()/dismissPage()/resetPage() is the preferred way.
I have a project that uses legacy code in .NET and WebForms. The legacy code uses several Update Panels.
My hope is to use AngularJS without impacting the legacy code base.
<div ng-app="myApp">
<NS:TheUserControl ID="TheUserControl1" runat="Server" />
</div>
Here is the javascript:
var app = angular.module("myApp", []);
app.run(function ($rootScope, $compile) {
function insertDirective() {
jQuery(targetElementSelector).attr("my-directive", "");
}
insertDirective();
var mgr = Sys.WebForms.PageRequestManager.getInstance();
mgr.add_endRequest(function (sender, args) {
insertDirective();
$compile(jQuery(targetElementSelector))($rootScope);
});
});
The code above adds an attribute to an HTML element inside NS:TheUserControl, and the attribute specifies a directive (see directive below).
Then, these steps are used when the update panel changes:
Detect the change using Sys.WebForms.PageRequestManager (EndRequest listener)
Re-add the directive using javascript
Run $compile on the newly-inserted directive
Here is the directive:
app.directive("myDirective", function () {
return {
template: "<span ng-repeat='item in items'>{{item}}</span>"
+ "<span ng-transclude></span>"
, transclude: true,
, controller: function ($scope) {
$scope.items = ["a", "b", "c"];
}
};
});
This almost works... except...
Problem 1:
AngularJS creates a scope associated with each instance of a directive. When the update panel inside NS:TheUserControl changes the DOM, the previous scopes are still present. I can see this in Batarang (a Chrome developer tool for AngularJS).
Problem 2:
For a brief moment after the update panels change, I get:
abc <-- Initial page load
abcabc <-- Subsequent update panel changes
abcabcabc
abcabcabcabc
Then, a moment later after each update panel change, the content jumps back to the correct:
abc
Questions:
So, how do I either:
Remove the orphaned scopes?
Or, incorporate AngularJS directives in a way that plays nice with the update panels?