I am not using Angular views or router. simply ng-include . I was able to get lazy loading with ng-include (initializing the templates with dummy div) and setting and updating it after getting template from server and updating templateCache.
The problem was most of the time templates where not updated.
Also i want allow user to see the template if the user has permissions. Permissions will be checked on server.
What is the best way to achieve this ?
I found the following solution from the following link
// CacheTemplate buster.
//
// Clears on successful route change (see later) the previous page template
// from the cache, like that the application DOES SEND another request to
// the server which in turn has an opportunity to chech access-rights of
// the user/session
$scope.tC = $templateCache;
$rootScope.$on('$routeChangeSuccess', function(event, next, current) {
switch( $location.path() ) {
case '/login' :
$window.document.title = "LOGIN";
$scope.loggedIn = false;
break;
case '/home' :
$window.document.title = "HOME";
$scope.loggedIn = true;
break;
default:
$window.document.title = "";
$scope.loggedIn = false;
break;
};
// clear cache to trigger reloading
// (server decides if has permission or not)
if (current && current.$route) {
if ($scope.tC.get(current.$route.templateUrl)) {
$scope.tC.remove(current.$route.templateUrl);
};
};
});
...
Related
It's two page user registeration process depending on the role the second page could be different but the first page will always remain the same. what I want I that user can go forward and backwards on both screens with persistent data. I trying a static page at start and then hide it and add the second template from route.
This is my angular app controller.
app.controller('addlandlordController' , function($scope , $http , $route ,API_URL , $routeParams , uploadService ){
$scope.API_URL = API_URL;
$scope.landVisible = true;
$scope.IsVisible = true;
if( $routeParams.test)
{
scope.$apply(function () {
$scope.IsVisible = false;
});
alert( $routeParams.test);
}
$scope.adduser = function($route){
var data = $.param({
fName: $scope.firstName,
lName: $scope.lastName,
role: 'landlord',
email: $scope.email,
linkId: $scope.linkId,
password: $scope.password,
});
var config = {
headers : {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8;'
}
}
$http.post(API_URL + 'user' , data , config)
.then(
function(response){
//$scope.IsVisible = false;
//alert('success');
},
function(response){
// failure callback
alert('fail');
}
);
}
});
I have a div in html like this,.
<div id="content" class="container1" ng-controller='addlandlordController' >
<div ng-show = "IsVisible">
And following is my route in config,.
app.config(function($routeProvider){
$routeProvider.when('/landlord' , {
templateUrl : 'template/addlandlord.html',
controller : 'addlandlordController',
resolve: {
test: function ($route) { $route.current.params.test = true; }
}
})
});
What I want is that when the user click on the following button.
Create an Account</button>
On click that button #/landlord will be added to the url and the route config code will run and add the other template in ng-view which is happening. Now next step is to hide the old div above in such a way that when user go back one sten only the previous code should show and when user goes again into the next screen only the next template should be visible and mean while data should remain same for the both views.
Issues I am facing is
Css is for template view is missing although the css files are already in the commen header. But appears when a place css in the style within template.
if I hide the first div in the response of adduser then if user go back it still hidden. it doesn't appears unless I refresh the page.
But if went to hide it through route config the value turn false but div never hides.
Please check this
scope.$apply(function () {
$scope.IsVisible = false;
});
You are using $apply on scope, but not in $scope.
And $applyAsync is preferable method to trigger digest without risking of error "$digest already in progress"
$applyAsync example:
$element.on('click', ()=>{
$scope.model.testValue = 'I have been updated not from angular circle';
$scope.$applyAsync();
});
Link to the docs
Nice article to read
How to use $state.go() if I have just the URL ?
Or can I get a state based on URL? (and than use $state.go(state))
I'm asking because I had to intercept the $urlRouterProvider.otherwise() to wait for an other plugin loads some external modules.. and now I need to continue and call the URL that call otherwise()
In place of $state.go(), you can use $location service as well.
i.e.
$location.path(url)
Please take care of not using # in URL. You can also use window.location.href
I had a similar problem, and $location wasn't helping, so I wrote a function to get the state from the url.
NB: I am using nested states based on ui-router.stateHelper, so I traverse my nested states object, testing for url matches. It would be slightly different when using dot notation to define nested states - and even easier if you don't use nested states at all!
function goPath (path) {
var target;
var arr = path.match(/\/\w+/g);
var i = 0;
var testState = function (state, i) {
if (state.url === arr[i]) {
target = state;
if (state.children && state.children.length && arr.length > i+1) {
i++;
state.children.forEach( function (childState) {
testState(childState, i);
});
}
}
};
myStatesObj.forEach( function (state) {
testState(state, i);
});
$state.go(target.name);
};
I was on a similar situation, what I did is changed the location to a different path and reset it to the current after a timeout like this
var path = $location.path();
$location.path("/");
$timeout(function(){
$location.path(path).replace(); //use .replace() so the empty path won't go to the history
},0);
i'm adding a full answer to this due to the high number of views.
NOTE: location.search() is only used where you need to handle a URL with a query string in it. otherwise use location.path() only.
your ui.router login state should look something like ...
.state('login', {
url: '/login',
templateUrl: 'routes/login/login.html',
controller: 'LoginController',
controllerAs: 'loginCtrl',
authenticate: false,
params: {
fwdPath: undefined, // Location to forward to on login success
fwdQueryStringObject: undefined // Query string object to use on login success - retrieved from $location.search()
}
})
your 401 (unauthorised) interceptor should look something like ...
state.go('login', {fwdPath: location.path(), fwdQueryStringObject: location.search()});
your login controllers login function should call your login service's login function. the code INSIDE the controllers login function should look something like ...
loginService.login(self.username, self.password).then(function (response) {
// local vars prevent unit test failure
var fwdPath = state.params.fwdPath;
var fwdQueryStringObject = state.params.fwdQueryStringObject;
if (response.status === 200) {
timeout(function () {
if (fwdPath != null) {
location.path(fwdPath).search(fwdQueryStringObject);
location.replace();
} else {
state.go('home');
}
}, 400);
} else {
self.error = true;
}
self.pending = false;
}
};
and finally your unit tests ...
state.params.fwdPath = '/login/list';
state.params.fwdQueryStringObject = {q: 5};
spyOn(location, 'path').and.callThrough();
spyOn(location, 'search').and.callThrough();
...
expect(location.path).toHaveBeenCalledWith('/login/list');
expect(location.search).toHaveBeenCalledWith({q: 5});
I've been digging through the $location.path postings for the past few hours, with no success. I think this example is really close, but I can't find what I'm missing.
$location.path() calls are returning my current path the first time they are clicked, then undefined. When I try to route with $location.path('/') nothing happens.
I have a soft button, bound to $rootScope.backBehavior that works correctly in iOS, but the below binding for Android doesn't work. I've tried doing an $injector.get('$location'); as well but I get the same behavior.
$rootScope.backBehavior = function() {
console.debug("Back button pressed for path: " + $location.path());
//For certain pages we navigate to home page on back button
var homeRoutes = ['contactus', 'aboutus'];
$.each(homeRoutes, function( index, value ) {
if($location.path() == ('/'+value)) {
console.debug("change path to: /");
$location.path('/');
return;
}
});
var backDisabled = ['mustDoSomething'];
$.each(backDisabled, function( index, value ) {
if($location.path() == ('/'+value)) {
console.debug("Back button disabled for route: " + value);
return;
}
});
if($location.path() == '/complexRoute'){
console.debug("Change route to: /anotherPath");
$location.path('/anotherPath');
return;
}
};
// Android back button support
WL.App.overrideBackButton(function(){
$rootScope.backBehavior();
});
It looks like this is the same issue here:
Angular $location.path not working
Try to run the expression as function in the $apply() method:
$rootScope.$apply(function() {
$location.path("/");
});
Try to use $location.replace() after changing the $location.path .
Is there any way to silently change the route in the url bar using angular?
The user clicks a link for the email that goes to:
/verificationExecuted?verificationCode=xxxxxx
When the page loads I want to read the verificationCode and then clear it:
if($location.path() == '/verificationExecuted'){
this.registrationCode = this.$location.search()["verificationCode"];
this.$location.search("verificationCode", null); //Uncomment but make this silent!
if(registrationCode != null) {
....
}
else $location.path("/404");
}
What happens when I clear it is the remaining part of the route ("/verificationExecuted") remains buts the route re-triggers so it comes around again with no verificationCode and goes straight to 404.
I want to remove the code without doing anything else.
You can always set the reloadOnSearch option on your route to be false.
It will prevent the route from reloading if only the query string changes:
$routeProvider.when("/path/to/my/route",{
controller: 'MyController',
templateUrl: '/path/to/template.html',
//Secret Sauce
reloadOnSearch: false
});
try this
$location.url($location.path())
See documentation for more details about $location
I had a similar requirement for one of my projects.
What I did in such a case was make use of a service.
app.factory('queryData', function () {
var data;
return {
get: function () {
return data;
},
set: function (newData) {
data = newData
}
};
});
This service was then used in my controller as:
app.controller('TestCtrl', ['$scope', '$location', 'queryData',
function ($scope, $location, queryData) {
var queryParam = $location.search()['myParam'];
if (queryParam) {
//Store it
queryData.set(queryParam);
//Reload same page without query argument
$location.path('/same/path/without/argument');
} else {
//Use the service
queryParam = queryData.get();
if (queryParam) {
//Reset it so that the next cycle works correctly
queryData.set();
}
else {
//404 - nobody seems to have the query
$location.path('/404');
}
}
}
]);
I solved this by adding a method that changes the path and canceling the event.
public updateSearch(){
var un = this.$rootScope.$on('$routeChangeStart', (e)=> {
e.preventDefault();
un();
});
this.$location.search('new',search.searchFilter);
if (!keep_previous_path_in_history) this.$location.replace();
}
I began learning Backbonejs recently, by reading a book. and I feel a little bit confuse about this issue.Here is a Router:
define(['views/index', 'views/login'], function(indexView, loginView) {
var SelinkRouter = Backbone.Router.extend({
currentView: null,
routes: {
'home': 'home',
'login': 'login'
},
changeView: function(view) {
if(null != this.currentView)
this.currentView.undelegateEvents();
this.currentView = view;
this.currentView.render();
},
home: function() {
this.changeView(indexView);
},
login: function() {
this.changeView(loginView);
}
});
return new SelinkRouter();
});
and this is the boot method of a application:
define(['router'], function(router) {
var initialize = function() {
// Require home page from server
$.ajax({
url: '/home', // page url
type: 'GET', // method is get
dataType: 'json', // use json format
success: function() { // success handler
runApplicaton(true);
},
error: function() { // error handler
runApplicaton(false);
}
});
};
var runApplicaton = function(authenticated) {
// Authenticated user move to home page
if(authenticated) window.location.hash='home';
//router.navigate('home', true); -> not work
// Unauthed user move to login page
else window.location.hash='login';
//router.navigate('login', true); -> not work
// Start history
Backbone.history.start();
}
return {
initialize: initialize
};
});
My question is about the runApplication part. The example of the book that I read passed router into module just like this, but it used window.location.hash = "XXX", and the router wasn't touched at all.
I thought the "navigate" method would make browser move to the page I specified, but nothing happened. Why?
And for the best practice sake, what is the best way to achieve movement between pages(or views)?
thanks for any ideas.
You could also use the static method to avoid router dependency (while using requirejs for instance).
Backbone.history.navigate(fragment, options)
This way, you just need :
// Start history
Backbone.history.start();
// Authenticated user move to home page
if(authenticated)
Backbone.history.navigate('home', true);
// Unauthed user move to login page
else
Backbone.history.navigate('login', true);
According to the documentation, if you also want to call the function belonging to a specific route you need to pass the option trigger: true:
Whenever you reach a point in your application that you'd like to save
as a URL, call navigate in order to update the URL. If you wish to
also call the route function, set the trigger option to true. To
update the URL without creating an entry in the browser's history, set
the replace option to true.
your code should look like:
if(authenticated)
router.navigate('home', {trigger: true});
Once your router is created, you also have to call
Backbone.history.start();
Backbone.history.start([options])
When all of your Routers have
been created, and all of the routes are set up properly, call
Backbone.history.start() to begin monitoring hashchange events, and
dispatching routes.
Finally the runApplication logic will be something similar to this:
var runApplicaton = function(authenticated) {
var router = new SelinkRouter();
// Start history
Backbone.history.start();
// Authenticated user move to home page
if(authenticated)
router.navigate('home', true);
// Unauthed user move to login page
else
router.navigate('login', true);
}