SOLVED
the trick was using window.location = '....';
I'm new to the AngularJS and I'm experiencing problems with redirecting after saving form data.
when I click 'Save' button, I send the data to the server, which saves it to the database.
$scope.save = function (trips) {
if (trips.length != 0) {
tripsRepositorySave.save(trips).then(function () {
$location.url('/Trips?id=' + trips[0].id);
});
}
};
this is the tripsRepositorySave
app.factory('tripsRepositorySave', function ($http, $q) {
return {
save: function (trips) {
var deferred = $q.defer();
$http.post('/Trips/Save', trips).success(function () { deferred.resolve(); });
return deferred.promise;
}
}
})
and the result is this url
http://localhost:3333/Trips?assignmentID=2#/Trips?assignmentID=2
I cant manage to make it rewrite the whole path, I have tried $location.path, $scope.apply() and everything i could google, but the result is still the same.
Another thing is that I want the page to get reloaded as well and it doesnt seem to be happening.
Thank you for any suggestions :)
You want something like this:
$location.path("/Trips").search('id', trips[0].id);
Also see the hash function if you need to append a hash.
https://docs.angularjs.org/api/ng/service/$location
In addition to Andy Gaskell answer.
$location.url(...) will manage only part after '#'. So your code works as expected.
If you want to reload full page use window.location or $window
Related
I am using AngularJS, ui-router and $resource for RESTful webservices.
A button in html view is clicked that calls below function i.e. $scope.login(). Consequently a REST service (through $resource) is called and returns a user in case user/pass are correct,
$scope.login = function() {
myfactory.get({
email: $scope.user.email,
password: $scope.user.password
},function(user) {
accessmgr.grantAccess(user); //Line of interest - loi1
$state.go('app.dashboard-v1'); //Line of interest2 - loi2
}, function(x) {
if (x.status == 401)
$scope.authError = 'Email or Password not right';
else
$scope.authError = 'Server Error! Are you connected to internet?';
});
}
in case above successfully executes, another factory function (loi1 above) is called to store user instance in $localStorage as below;
myapp.factory('accessmgr', function($localStorage) {
//var User = {};
return {grantAccess: function(usr) {
$localStorage.user = usr;
}
}});
and ui-router $scope.go(...) takes the user to dashboard.
Problem:
Sometimes $state.go(...) executes before accessmgr.grantAccess(...) causing exceptions as the new state reads user from $localStorage that is not yet written. Reload the page manually solves the problem.
Any help would be really appreciated.
localStorage itself works in synchronous manner, but ngStorage's $localstorage doesn't. The latter is intended to be used in conjunction with scope and is tied to Angular digest cycles. My guess is that
myapp.factory('accessmgr', function($localStorage) {
return {grantAccess: function(usr) {
$localStorage.user = usr;
$localStorage.$apply();
}
}});
may help. ngStorage doesn't really shine when being used like this, probably JS generic library like store.js applies better.
A good alternative is to use model that acts as single source of truth and dumps the data to localStorage under the hood. Depending on the scale of the project, js-data-angular can be considered a solid solution for that.
ngStorage's $localStorage cannot be referred directly without using watchers (not recommended as per here, alternatively it can to be passed as a reference to hook to $scope as mentioned as recommended approach here.
For me, I was using $localStorage through a factory and I tied it to rootScope as below;
$rootScope.$storage = $localStorage;
and consequently
myapp.factory('accessmgr', function($localStorage) {
$rootScope.$storage = $localStorage;
return {
grantAccess: function(usr) {
$rootScope.$storage.user = usr;
},
getUser: function() {
return $rootScope.$storage.user;
}
}});
I have a service with the following:
.service('TasksService', function ($http) {
return $http.jsonp('http://blabla/json/?callback=JSON_CALLBACK');
});
My controller:
.controller("TasksCtrl", function ($scope, TasksService) {
TasksService.then(function (TasksService) {
$scope.Tasks = TasksService;
});
});
This shows list with many items.
But I have another service and another controller. I change an item there and when I save and post data to database and I get back to initial list, I still see the old listed items. I must refresh the browser to see what I have edited.
In the second controller I have save function with this:
$scope.saveTask = function () {
$http.post('http://blabla', $scope.Task.data).then(function (data) {
// This shows the very first state with all the listed items
$state.go('tasks');
});
};
When I save the changes and state.go() shows me the first screen I do not know why the data is not with the new changes from the service returned data. It seems like the screen is just changed but the new data is not returned from the service. Any ideas how to fix this?
I got it working. I changed my service and controller. In my service I returned an object with another method and then I changed my controller also to use the returned method.
The service:
.service('TasksService', function ($http) {
return {
getTasks: function() {
return $http.jsonp('http://blabla/json/?callback=JSON_CALLBACK');
}
};
});
And the controller:
.controller("TasksCtrl", function ($scope, TasksService) {
TasksService.getTasks().then(function (data) {
$scope.Tasks = data;
});
});
Now it is working and I see the items list the new saved and returned data. Thank you!
All you need is to reflect the changed data into your html
Angular has a method for this
$scope.$apply();
This method works both with parameters as well as without parameters.
Hope this helps.
While saving a new invoice using my AngularJS app, there is a noticeable time taken while the API is checking products balances, saving data...etc so I was wondering is there a way in AngularJS where I can show like an intermediate (page...example: #/processing) where the users get routed to once the user click the Save button then depending on the save new invoice $http result (failure or success) either route the user back to the invoice page (ex. #/new-invoice) OR the success saving page (#/thanks-for-ordering) ?
Any example is highly appreciated. Thanks
i am using spinner for such things... on http request start the spinner and on response stop it. use the http interceptor for the same. if you dont want to implement it yourself, below are few links.
angular-spinner or angular-sham-spinner
also read this BLOG which details how the spinner works
You can use ng-if to switch between a input and saving state.
Example template:
<div ng-if="isSaving"><img src="spinner.gif"> Saving...</div>
<form ng-if="!isSaving" ng-submit="saveTheThing(thing)>
<input ng-model="thing.title" type="text"/>
...
</form>
Example controller:
angular.module('app').controller('ExampleCtrl', function ($scope, $location) {
$scope.saveTheThing = function(thing) {
$scope.isSaving = true;
$http.post('api/things', thing).then(function (response) {
$location.path('/things/' + response.data.id); // Go to the success page
}).catch(function (error) {
$scope.error = error; // show error
}).finally(function () {
$scope.isSaving = false;
});
};
})
If you need IE8 support you'll need to replace .catch with ['catch'] and .finally with ['finally']
Although I wouldn't use a standalone url/route for a processing page.
You can store the save promise in a service.
angular.module('app').value('progress', {});
angular.module('app').controller('FormCtrl', function ($scope, progress, $location) {
$scope.saveTheThing = function(thing) {
progress.thing = $http.post('api/things', thing);
$location.path('/processing');
});
});
angular.module('app').controller('ProgressingCtrl', function (progress, $location) {
progress.thing.then(function () {
$location.path('/thank-you');
}, function (error) {
$location.path('/form');
});
});
Compared to the ng-if solution you now need additional work to:
Restore the form values on failure
Pass the error message to the FormCtrl
Define the refresh behavior for the progressing page
I have a checkbox, like:
<input type="checkbox" ng-model="isPreCheckIn" />
I'm getting isPreCheckin (boolean) from a service which uses $q and either returns from the server or localStorage (if it exists).
The call in the controller looks like:
deviceSettings.canCheckIn().then(function (canCheckIn) {
$scope.isPreCheckin = !canCheckIn ? true : false;
});
And deviceSettings.canCheckIn looks like:
function canCheckIn() {
var dfrd = $q.defer();
LoadSettings().then(function (success) {
return dfrd.resolve(localStorage.canCheckIn);
});
return dfrd.promise;
};
So, on first page load, the checkbox doesn't bind correctly to isPreCheckIn; in fact, if I do a {{isPreCheckIn}}, it doesn't either. If I switch off of that page and go back, it works.
It appears that canCheckIn is outside of angular, based on that assumption, you need to wrap your assignment within $scope.apply:
deviceSettings.canCheckIn().then(function (canCheckIn) {
$scope.$apply(function(){
$scope.isPreCheckin = !canCheckIn ? true : false;
});
});
This tells angular to recognize the changes on your $scope and apply to your UI.
I think you should wrap the following in a $apply:
function canCheckIn() {
var dfrd = $q.defer();
LoadSettings().then(function (success) {
scope.$apply(function() {
dfrd.resolve(localStorage.canCheckIn);
}
});
return dfrd.promise;
};
It sounds like a timing issue. You may need to put a resolve clause in your route to give this call time to run and then pass in the result as a DI value. Without knowing which router you are using it is impossible to give you an accurate answer, but you might look at the video on egghead.io regarding routes and resolve.
I'm trying to developpe a chrome extension with angularjs and I have a strange behaviour when I try to initialize the $scope with the url of the active tab.
Here the code of my controller:
var app = angular.module('app', ['app.service']);
app.controller('ItemCtrl', function ($scope, chromeHelper) {
$scope.website = "No result!";
// Does not work until I click on something :-/
chromeHelper.getActiveTabDomain(function (domain) {$scope.website = domain; });
});
So when I try to initialize directly the $scope.website member it doesn't succeed but when I click on the button aftewards $scope.website then updates.
I really don't understand why.
Here is the code of my Chromehelper service:
var service = angular.module('app.service', []);
service.factory('chromeHelper', function() {
var chromeHelper = {};
chromeHelper.getActiveTabDomain = function (callback){
chrome.tabs.query({'active': true}, function(tabs){
if(tabs && tabs.length > 0) callback(getDomainFrom(tabs[0].url));
});
};
return chromeHelper;
});
function getDomainFrom(url) {
return url.match(/:\/\/(.[^/]+)/)[1];
}
Thank you very much in advance!
The OP solved the problem (see comment above) by adding $scope.$apply() at the end of the callback:
// Does not work until I click on something :-/
chromeHelper.getActiveTabDomain(function(domain) {
$scope.website = domain;
$scope.$apply(); // <-- adding this line did the trick
});
A short explanation for anyone landing on this page with a similar problem:
From the AngularJS docs on 'scope' (more specifically from the section titled 'Scope Life Cycle'):
Model mutation
For mutations to be properly observed, you should make them only within the scope.$apply(). (Angular APIs do this implicitly, so no extra $apply call is needed when doing synchronous work in controllers, or asynchronous work with $http or $timeout services.
See, also, this short demo.