I'm using angular in an application which is, basically, a table with search results.
Access to this table can be achieved via an url like http://myapp/?client=clientName
An angular controller is instantiated for the table, among other things, for opening a modal dialog (also angular-based with bootstrap-ui) with the row details.
These row details are brought via a service which has some common functionality for both controllers: the one for the table and the one for the modal.
Now, within this service, I have the following snippet to retrieve:
service.fetchRelatedElements = function(element, cb) {
var url = '/search.json?results=20&type='+element.type;
if ($location.search()['client']) {
url += '&client=' + $location.search('client');
}
return doFetch(url, cb); // actual server json GET
};
The goal is to know if the table already has this specific client parameter set as a filter.
If I put a breakpoint at the beginning of this call, I see that $location.absUrl() returns the current browser URL (which, in my case, has the client parameter I'm interested in).
But $location.search() returns an empty object.
I am injecting the $location service within my service with the defaults (that is, not configuring it by a .config() call).
And, as doc says:
The $location service parses the URL in the browser address bar (based
on the window.location) and makes the URL available to your
application.
Am I missing something? Shouldn't the URL, at this point, be parsed?
Thanks!
UPDATE: I've managed to make it work. The problem was exactly that I wasn't configuring at all the service. I did so because I assumed that in that way it would take defaults, but it seems that that's not the way it works.
I was having the same problem before I configured $locationProvider in my app's module config:
appModule.config(['$locationProvider', function($locationProvider) {
$locationProvider.html5Mode(true);
}]);
If you don't want to specify the base tag, you can specify require base false.
myapp.config(function($locationProvider) {
$locationProvider.html5Mode({
enabled: true,
requireBase: false
});
});
When I encountered this problem, another thing that worked for me besides setting the configuration is to add "#" in front of the query string.
So if you are the one creating the query string, then changing
myapp/?client=clientName
to
myapp/#?client=clientName
allowed $location.search() to give me a non-empty object which you can then access each parameter using $location.search()['client']
The API for $location.search is pretty confusing. Calling
$location.search('client');
will set the search object to {client: true} and return $location. Furthermore, you have a typo client instead of 'client', so it's setting search to an empty object. So you probably want:
url += '&client=' + $location.search()['client'];
You can write a function that parses the $window.location.search based on this comment https://github.com/angular/angular.js/issues/7239#issuecomment-42047533
function parseLocation(location) {
var pairs = location.substring(1).split("&");
var obj = {};
var pair;
var i;
for (i in pairs) {
if (pairs[i] === "")
continue;
pair = pairs[i].split("=");
obj[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
}
return obj;
}
$scope.query = parseLocation($window.location.search)['query'];
Related
I have looked up several posts with similar issue but was unable to find a solution for my case.
I have a search page that sends a request to the backend an populates the page with search results (each result is an object). I display a concise view of each object and, upon a mouse click on a specific object, the user should be redirected to a page that shows a more detailed view for that object.
On my JS side, I have one controller that handles the $http.post call and retrieves the objects from the backend to display on the first page. I use a different controller for the second page to try and get the relevant object from the first controller (through angular's .service ), but for some reason I get an empty object on the second page. The service works with a getter and a setter. The service is able to set the object just fine, through the first controller, and I am able to print it. However, when redirecting to the second page, while using the second controller's getter, the object gets deleted and shows as empty for some reason.
Here is the relevant code. The service:
app.service('shareService', function(){
var savedData = {}
function set(data) {
savedData = data
console.log(savedData);
}
function get() {
console.log(savedData);
return savedData;
}
return {
set: set,
get: get
}
});
The search (setter) contoller:
app.controller('SearchCtrl', function(shareService, $scope, $http) {
$scope.sendSearch = function() {
$http.post("http://localhost:9080/MedNetApp/rest/mednet/searchCollections", json).success(function (response) {
$scope.collections = response.searchResults;
shareService.set($scope.collections);
});
};
});
The second (getter) controller:
app.controller('CollectionsCtrl', function(shareService, $scope){
$scope.collections = shareService.get();
})
Not sure if this is relevant, but here is also the html part where I set up a temporary test button to redirect to the second page:
<button id=mixing type = "button" class="btn btn-primary-aligned"
data-ng-click = "go('second-page.html')">temp</button>
So, at the end, savedDatashows as empty object when printing it the second time through the get() function. Any idea why this is not working? or a better way to send data to a new page?
EDIT - I should mention that I basically relied on this solution:
AngularJS - Passing data between pages
So, after further research, I came across this solution:
Sharing Data between pages in AngularJS returning empty
I used sessionStorage in my service, as described in the link above, and it fixed the problem.
Use localStorage or sessionStorage
var myJson ={};
localStorage.set("your_Data",myJson);
// to get the value from another page
var returnJson = localStorage.get("Your_Data");
I have ui-router set up as follows
.state('root.event', {
url : '/event/:id',
templateUrl : 'templates/event.html'
})
(the controller is initiated in the template)
which gives nice looking Basic URL Parameters like:
www.mysite.com/event/1234
When a user navigates directly to my www.mysite.com/event path (ie param is missing) the template controller looks the most recent id parameter from either:
- a js variable stored in a value
- localstorage / cookie etc
I then return this to my state using $location.search('id', 1234)
...however, this results in URLs which have Query URL Parameters like:
www.mysite.com/event/?id=1234
Is there a technique to ensure that $stateparams updates present the url in basic format on update ?
...or is it possible to get the URL parameter & update the $state before the state change ?
(I looked at Resolve but this seems mostly to be about passing parameters to controllers)
I've had a look here, here and here - but most of the questions relate to how to avoid reload on update of $state params
$location.search does exactly that. It adds query URL parameter. I think what you're looking for is
$state.go('root.event', {id: 1234})
I'm fairly new to AngularJS and I realise this question has been asked before (so hopefully I wont get voted down) but I've been unable to find a question matching my situation.
I want to pass search data (about 4 fields) from a search page (My HomeCtrl) to a ListingCtrl. The listingCtrl already uses a factory/service to get info based on whats passed from an API (I've hardcoded the data at present).
Now everything I read says uses another server/facotry to pass the data between the search page and the listing page but (and forgive me if I'm missing something) why not just pass these additional params as routeParams??
Incase you need to know - I'm using partials and route params to navigate around the app and pass a few simple IDs between pages.
I suggest to use a service.
If you want stock persistant data, i suggest to use this https://github.com/Zmetser/localstorageservice
This module uses localStorage (documentation) (or cookies if you use IE)
Untested example:
The service
myApp.service('controllerSharingData', function() {
var __variables = {};
return {
get: function(varname) {
return (typeof __variables[varname] !== 'undefined') ? __variables[varname] : false;
},
set: function(varname, value) {
__variables[varname] = value;
}
};
});
Controllers
myApp.controller('IndexCtrl', function($scope, controllerSharingData) {
controllerSharingData.set('toto', 'hello world');
});
myApp.controller('ListCtrl', function($scope, controllerSharingData) {
alert(controllerSharingData.get('toto'));
});
I have a backend object that i receive from the server and sent to server.
I give a permission to my users to change the backend object.
I would like to identify if the backend object has changed from the save point.
How can i identify it?
For example:
I have a module called
app.controller('PanelCtrl', function($scope, $http) {
$scope.action = "";
$scope.selectedItem = "";
$scope.compoundItem = [];
...
I have in the compound item array of Objects.
I would like to know if something is changed whether the change occures in the primitives and whether in the compoundItem...
Edit: two scenarios 1) client check (the original post) 2) server check (extended based on the comments)
1) check on the Client
do not worry about performance of the below steps. And if you do, please read more about performance here
The answer is: angular.equals() and a similar scenario is described here: Developer Guide / forms (it is about validation). In the section Binding to form and control state, we can see the script (an extract):
function Controller($scope) {
$scope.master = {};
$scope.update = function(user) {
$scope.master = angular.copy(user);
};
$scope.reset = function() {
$scope.user = angular.copy($scope.master);
};
$scope.isUnchanged = function(user) {
return angular.equals(user, $scope.master);
};
}
What we can see here, is what we need. Firstly copy the source state (inside the update function). We can change isUchnaged any time, while using the angular.equals()
Also check Compare objects in Angular
NOTE: A comment to the angular.copy(). I found, that in some cases, better is to use the lo-dash .deepClone()
2) Server side solution
In case that (as Dalorzo expects) are interesting in the comparison of the server, persisted version and the client. It could make sense, if we want to check if some one else has already changed the "Entity". In some other transaction
There are in general many techniques, but the most effective is versioning. On the persistence layer, we have to introduce some value, which is changed/incremented every time the "Update" is executed. In case of MS SQL Server it would be the rowversion, see more here
Then, during any sucesfull UPDATE operation, this version will be changed, and we know that data on the client are stale... there are newer on the server.
Summary with version:
Our check could be very easy:
Compare the Client (current) Version property and
ask the server for the latest persisted.
This is a standard way I am using with NHiberante (read more here)
The first solution that comes to my mine is to use
$timeout(isDifferent,xtime)
where:
isDifferent= is your custom function to get the backend object or identify when it has changed.
xtime= is and integer value in milliseconds which represents how often your function will execute.
In Angularjs app, i have a url like http://url.com/my_app/#/store/items.
Now i want to append query string for example, http://url.com/my_app/#/store/items?page=2. but in url, javascript encodes the "?" to "%3F" which i don't want. It should remain "?" only in the url as angularjs $location.search() returns nothing for "%3F".
How it can be done ?
There is not enough details in your question so I will assume that you are using AngularJS routing - or at least the $location service - in non-HTML5 mode. If so, the part after the # character represents your URL from the single-page-application point of view (more about AngularJS here).
If the above assumptions are correct it means that you shouldn't try to add or manipulate the question mark "by hand". Instead you should change the search part of the $location to manipulate query string (part after ?) and the question mark will be added / removed to the final URL as needed.
In your case you could write:
$location.path('/store/items').search('page', 2)
This is assuming that you are manipulating URLs from JavaScript, as stated in your question.
If you are using the $location service then use $location.url('/store/items?page=2') instead. This has been a setter method from at least 1.0.7 and works a treat in my 1.1.5 app.
you can create a parameter object like:
var param = {
page: 2
}
$location.url("/store/items").search(param)
If you're using the ui-router which is highly recommended, you could use $state.go(to, params, options) as described here.
As prerequisite you need to define your state properly, that means every possible query parameter must be made known to the ui-router. See the following example (page and otherParam):
$stateProvider.
state('storeItems', {
url: '/store/items?page&otherParam',
templateUrl: '/modules/store/views/item.client.view.html'
});
And then you can just switch locations for instance from a controller by calling
$scope.gotoItemsPage = function(page) {
$state.go('storeItems', {
page: page,
otherParam: 'Just a show off'
});
};
No fiddling with the encoding needed and highly readable!
You can use decodeURIComponent.
For example:
decodeURIComponent('http://url.com/my_app/#/store/items%3Fpage=2');
// will give you `http://url.com/my_app/#/store/items?page=2`