Angular simple routing without templates - javascript

I am looking for simple solution with Angular how to handle routes.
I have page generated by server with simple google map and little logic which is in 3 separated controllers.
Now i want to hook routing inside of this. Just simple routing. When I move with map and get new coordinas i want to push them into current url as param "?position=10.11,50.23". I want to use history push state with hashbang backup.
In other part of application i want to listen on change of this parameter (some $watch maybe?). And i want to use same code for detecting change to be used when page is loaded first.
I used CanJS in previous project, where this was absolutely simple, but in Angular i cant event make baby step to correct result :)
PS: this is just cut of minimal usecase.
Thanks

You do not really need angular routing to do that. Routing in angular is usually used to replace ng-view with different templates though it can be used for other things as well.
Your requirements can be met by using the $location service and setting up a $watch:
$scope.$watch(function () { return $location.search().position; }, function (newVal) {
$scope.params = newVal || 'No params';
});
$scope.setParams = function (val) {
$location.search('position', val);
};
Working demo
Launch the live-preview in a separate window to see the parameters change in the window address bar.
Update: The Demo doesn't work on plnkr.co anymore. Possibly because they have changed how they use the embedded mode.

Do not know CanJS, but in angular it is pretty easy as well.
First use: $location.search({parameter1:'someValue', parameter2: 'someOtherValue'}) to set you url -- the history will be updated automatically.
Then the most elegant way to detect any changes in url is to use (one of) two built-in events:
$locationChangeStart and $locationChangeSuccess, like that:
$scope.$on("$locationChangeStart", function (event, next, current) {
// your staff goes here
});
$scope.$on("$locationChangeSuccess", function (event, next, current) {
// or here (or both if needed)
});
To get the search parameters just use $location.search() in one of the above methods.

Related

Is there a way to "watch" an IndexedDB record so I can respond when it's been changed?

I am building an Angular + TypeScript application that uses IndexedDB for storing data locally.
I have an Angular directive that sets the value of a scope variable to be some data that was returned from an IndexedDB request. It's pretty simple and does something like:
Directive A:
// getGroup() is making the requests to IndexedDB:
this.dataService.getGroup().then((groupOne) => {
this.scope.items = groupOne.items;
});
The view for my directive loops through each of these items:
<div ng-repeat="item in items">{{ item }}</div>
I have another Angular directive (let's call it Directive B) that is updating/inserting the items associated with groupOne.
Directive B:
groupOne.items.push("a new item");
this.dataService.updateGroup(groupOne).then(() => {
// groupOne has been changed!
// how can I let Directive A know about this, so Directive A can update its scope.items?
});
Of course, Directive A does not know about the changes Directive B made to groupOne unless it does another request. And therefore, my view is "static".
I know I could wrap Directive A's IndexedDB request into an interval and be checking for updates, but that seems like a strange way to solve this problem.
Is there a way with IndexedDB to be notified of this change? Is there something Angular provides that could help with this (something similar to $scope.$watch()) ?
It's not quite what I wanted (which is why I accepted dgrogan's answer), but in case anyone who stumbles upon this question is curious about what I ended up doing:
Manually broadcasting a custom event whenever I change groupOne (or anything else I care about) solves my problem for now.
I used $scope.$broadcast() from a controller (that's managing
a few interactions between directives) to let Directive A know about
the change Directive B made using $scope.$on().
This article was really helpful: http://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/
There is not yet a way to do this with pure IndexedDB but it is being prototyped. The name is "Observers". You can try to use the polyfill if you are so inclined.

how to get json data from a angular service using jquery

I am setting data from a angular controller to a service. I need to somehow get this data using jquery or javascript.Is this possible since I am not requesting it from a url?
Angular service
getSeriesData: function () {
return this.legendSeries;
},
setSeriesData: function (legendSeries) {
this.legendSeries = legendSeries;
},
Not sure why you would want to do that (if you explain the scenario better it might help), but in case you want to share data between an angular application and other JS code (e.g. JSON), one way you could go about is to dispatch a global event from the angular service [for example $(window).trigger('myEvent', myJSON] and then catch this on your separate JS code [window.addEventListener('myEvent'...]
Seems like possible using injector
var injector = angular.element('body').injector()
$injector.invoke(function (serviceName) {
var legendSeries = service.legendSeries;
service.setSeriesData(legendSeries);
});

Using shared module (between separate modules)

So I have a module I have created that does a kind of "state" routing for me. I made my own little version to get my exact intended effect, and it seems to be working great until I plug it into separate modules to test.
I inject it into the 2 separate modules, define the information in the .config of each module I need to use it, then call it in a controller to use my change state kind of effect.
It had been going pretty good until I plugged it into separate modules, and now what seems to be happening is the module I have created to handle all of this is creating separate instances for each module. Let me show you what I mean:
Here is an example of one of the modules using it for testing -
angular.
module('urlTesting2', [ 'urlTesting'])
.config(function($moduleObjectProvider) {
var callback = function(name, obj) {
console.log(name, obj);
}
$moduleObjectProvider.$get().set("module2", callback)
.addState("calender", ["day", "week", "month"]);
}).controller("testControl2", function($scope, checkUrl) {
$scope.addSecond = function() {
checkUrl.goState("module2", "calender", ["yes", "no", "maybe"]);
}
});
So it's injected, and in the config I call the provider and set a new modules with states. In the controller I just call goState. This works great when its just by itself. The issue is when I add a separate module in doing the same. I have a fiddle here showing the problem -
https://jsfiddle.net/7hn3ovgz/1/
So - I like to test this in my own browser window but fiddle seems to be the easiest way to share this. It will not change the actual url in the browser but it will still log all the effects.
Basically what I think is happening is when I click to change state in a module, it fires it twice and looks for the state in the other module too (which isn't there). My desired effect was that ALL modules setting a config would be all in one place. So when you do the .set - it just adds the object into a variable called currentModules in the provider. It seems like the configs are setting separate instances (like a closure) of this, instead of pushing all the config set() into one big object for reference.
Apologies if this is unclear, hopefully the fiddle will show clearly enough, and thank you for taking the time to read.
Seems like the issue is the injector for the provider, every time it is called it creates a new instance of that function, so all you should have to do is switch
function $moduleObjectProvider() {
var currentModules = {};
to
var currentModules = {};
function $moduleObjectProvider() {
or restructure the provider not to be an injected function if possible

Angular, setting up a callback function for updating between factory and controller

I'm not sure if i have completely wrapped my head around this idea - but I'll try my best to clearly describe what I am trying to do here.
I have a factory that changes and parses a URL for me, so I can pass params into a controller for use (that were stored in the url). This is sort of so I can save a state for the user and they can share it via copy'ing of a URL (send it to their friends or bookmark it or w/e).
I am trying to set up a factory (or service) that listens for locationChangeSuccess - so that if the user mofies the url and presses enter, it will refresh the scopes in the controllers. So here is what I have:
.factory("urlFactory", function($location, freshUrl, StateString){
//request to change new url
requestObj.requestState = function(moduleName, stateName, startVar){
}
//request item from url, via your module and state name
requestObj.parseState = function(moduleName, stateName){
}
I dropped the center out (if it is needed im happy to link), but those just get and set the url for me.
So in the controllers I do something like
$scope.mod2m3 = urlFactory.parseState("module2", "mod3");
$scope.mod2m4 = urlFactory.parseState("module2", "mod4");
So when they land on the page, they pull their state. This works great. However, now i'm trying to solve some edge case scenarios where maybe the user modifies the url.
So I can latch onto that even pretty easily with
.factory("urlWatcher", function($location, $scope){
var urlWatcher = {};
$scope.$on('$locationChangeSuccess', function(event) {
console.log("Asdsa");
});
return urlWatcher
});
However, where I am struggling is trying to determine a way where when this fires, it would connect the new value to the scope in the controller. It was suggested to me that a callback of some sort in the parse (set) function, but I am struggling with how to approach that. It would be super cool if I could set a way for this factory/service to re send the new value when it changes to the right place. Callback sounds good, however I don't know how to config this correct.
The easiest route would be to just do an
$scope.$on('$locationChangeSuccess', function(event) {
console.log("Asdsa");
});
In each controller and manually bind to each scope, but I am trying to make this as modular as possible (and thats also a ton of watchers on the locationchangesuccess). would be fantastic if I could figuire out a clean way to set the service/factory to listen once, and on change find the right module/controller and change the value.
I can't seem to think a clear route, so I would be very greatful for any insight to this issue. Thank you very much for reading!
If what you want is a publish/subscribe architecture, where publications are global and subscriptions have the same lifecycles as Angular scopes... then Angular events are what you're looking for. There's no point setting up an ad hoc communication system with callbacks and whatnut, that would just be partially reinventing events.
However, if you want to make the semantics more obvious / add flexibility, you can listen once to $locationChangeSuccess in a service and broadcast a custom event.
$rootScope.$on("$locationChangeSuccess", function (event) {
$rootScope.$broadcast('myCustomeEvent', {message: "Guys, time to refresh!"});
});
Then listen to this event in each of the scopes where it is relevant.
$scope.$on('myCustomeEvent', function (event) {
console.log("Asdsa");
});
If setting up the listening gets repetitive, by all means, factor it out in a function, which you can for example put in a service:
myApp.factory('someFactory', [function () {
return {
listenToLogAsdsa: function (scope) {
scope.$on('myCustomeEvent', function (event) {
console.log("Asdsa");
});
}
};
}]);
Then all you have to write in your controller is:
someFactory.listenToLogAsdsa($scope);
You can assign a variable in the scope to an object in the factory, that way it's bound to a reference instead of a value. Then, in your HTML you bind the reference to the DOM. urlFactory.parseState() should then save the result to said object, and return the key where it was saved.
For example:
In urlFactory:
requestObj.parseState = function(moduleName, stateName){
var key = moduleName+stateName;
this.urlContainer[key] = "www.example.com";
return key;
}
In the controller:
$scope.urls = urlFactory.urlContainer;
$scope.mod2m3 = urlFactory.parseState("module2", "mod3");
In your HTML:
{{urls[mod2m3]}}
This way, "urls" is bound to a reference, which angular watches for changes, and whenever you change urls[mod2m3], it will affect the DOM.
You can also just react to changes in the scope variables by watching them:
$scope.$watch('urls', function() {
//do something
});
NOTE: Since this is an object, you might need to use $watchCollection instead of $watch.

Getting state from URL string with Angular UI Router

I'm using state-based routing (Angular UI Router v0.2.7) in a project and looking for a way to get the current state (name) from a given URL string.
Something like:
$state.get([urlString]) returns stateName:String or state:Object
I need this method to check if a state exists to a given URL because not all URLs are mapped to a state in my project. Using Play Framework as backend, some URLs (e.g., login form) are not mapped to a state because they using different templates then the Angular (main) part of my application.
For those "none-Angular" pages (i.e., not covered by a state) I would do a reload. To identify URLs not covered by a state I need the method mentioned above. Planned to do it like this:
$rootScope.$watch(function() { return $location.path(); }, function(newUrl, oldUrl) {
if(newUrl !== oldUrl) {
if (!$state.get(newUrl)) {
$window.location.assign(newValue);
}
}
}
Already checked the docu but there is no such method.
Any ideas?
It's all or nothing. If you plan to use ui-router make sure all your URLs resolve to a state. If the state doesn't exist it will go to the otherwise state. It's possible to have optional parameters.
An alternative is to use .htaccess redirects to catch the URL and redirect you before it hits the ui-router.
Provide more details and we can see what the best option is.
Try using this will get the current sate name.
var stateName = $state.current.name;

Categories