I'm all but sure this is the best practice to achieve my objective (manage OAuth in AngularJS) but here it is what I've done and what doesn't work:
By using Hello.js (injected in Angular through angular-global-injector) I managed to open the OAuth popup window (in my case Google+) by using the following code in my controllers.js file.
hello.init({
google: MY_APPLICATION_ID
},{redirect_uri:'app/_partials/'});
$scope.doLogin = function(network) {
console.log('Calling hello ' + network);
hello.login(network,function(r){ // callback
console.log("login successful..");
});
};
The pop-up with login request opens (note that I had to use a "fake" index.html view since $routerProvider doesn't seem to work properly on URI-redict on the child window (page not found, but the URI /auth would world normally on the parent window..since all the others routers work flawlessly on it. So I can't only guess the problem is with the child window.).
This way instead the index.html is loaded since the redirect is done as AngularJS wasn't there.
Here is my route configuration.
$routeProvider.
when("/userLogin", {templateUrl: "_partials/userLogin.html", controller: "webClientController"})
.when("/dialInterface", {templateUrl: "_partials/dialInterface.html", controller: "webClientController"})
.when("/error404", {templateUrl: "_partials/error404.html", controller: "webClientController"})
.otherwise({redirectTo: '/error404'});
Here is the content of my popup window (the fake "index.html").
<!-- temp workaround to manage oAuth since routeProvider doesn't match the REDIRECT URI correctly -->
<script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
<script>
$("#go-to-dial-interface",opener.document).trigger('click');
window.close();
</script>
And here is the code of the view (loaded correctly by AngularJS) /userLogin
<md-content class="md-padding md-primary" style="height: 600px;padding: 24px;" layout-fill>
<a ng-click="doLogin('google')" class="zocial googleplus">Sign in with Google+</a>
</md-content>
<a id="go-to-dial-interface" href="#dialInterface" style="visibility:hidden;"></a>
Basicly the logic (and the problem) is:
The user clicks on the a tag which leads to execution of doLogin('google') (this calls the function in the controller which contains the call to Hello.js)
A Google account is chosen, afterwards there is the redirect (in this case to the "fake" index.html since I don't manage it with Angular routes
this index.html is loaded properly in the OAuth popup and the javascript code is executed correctly: trigger go-to-dial-interface's click() event and close the OAuth popup
The triggered click() event is supposed to cause a redirect in the main window to a new view (#dialInterface) but it doesn't: the current view (userLogin) is reloaded (or perhaps not..) and the new one doesn't (of course the URL in the browser address bar doesn't change)
Any better solution / improvement is also well accepted.
Thank you in advance
Do you also have the webClientController ready for the /dialInterface view?
Any console errors?
Related
In my MEAN application for nested views I am using angular-route-segment. Everything works fine with the nested views except for the initial default screen.
my first page (index.html) contains header & footer with buttons connect & disconnect. This theme is common in all the pages.
once I click connect, it should load all list of todos (this is where I get problem).
Here is my controller config code
app.config(function ($routeSegmentProvider) {
$routeSegmentProvider.
when('/todos', 's1').
when('/todo/:id', 's2').
when('/task/:id', 's2.task').
segment('s1',{
templateUrl: "public/views/todos.html"
}).
segment('s2',{
templateUrl : "public/views/todo.html"
}).
within().
segment('home',{
default:true,
templateUrl : "public/views/default.html"
}).
segment('task', {
templateUrl: "public/views/task.html",
dependencies: ['id']
})
})
index.html
<html ng-app="Todo">
<body ng-controller="ConnectioCtrl">
<button class="btn btn-primary" ng-click="connect()">Connect</button>
<button class="btn btn-danger" ng-click="disconnect()">Disonnect</button>
<div ng-if="connected" ng-controller="ToDoListCtrl">
Todo List
<div app-view-segment="0"></div>
</div>
</body>
</html>
This perfectly works fine for me. but the problem here is I am forced to give Todo List which is a hyperlink. I don't want this hyperlink to be present in my screen. All that I need is, when ever I click on connect it executes a function which checks all the conditions and returns connected = true, and then by default it should go to list of todos rather than giving a hyperlink Todo list to click on.
please let me know if I need to provide any additional info to bring clarity on this query.
thanks in advance!!
I'm trying to make a blog using AngularJS. The home page queries a third party service of mine that returns an array of all my articles/posts. I am displaying shortened versions of these posts on the home page, and want to have "read more" under each post that passes that post's ID through a URL parameter to another HTML page:
index.html:
<div ng-controller="blogCtrl" id="blog">
<div class="post" ng-repeat="post in posts">
<div class="header">
<h1>{{ post.fields.title }}</h1>
<p class="date">{{ post.sys.createdAt | date}}</p>
</div>
<p>{{ post.fields.body | cut:true:1600:' ...'}}</p>
read more
</div>
</div>
What do I need to do in post.html so that I can read the value of id in the URL parameter? Do I need to create a new angularJS app in post.html?
edit:
I've changed the read more link to <a href="post/{{post.sys.id}}"> and i am trying to set up the following route:
app.config(function($routeProvider){
$routeProvider
.when('/post/:postid',{
templateUrl: '/post.html',
controller: 'postCtrl'
})
});
However, clicking the "read more" link doesn't load up post.html, but instead a page that says File not found: /post/2B1K9K2DHqsYaGYcms2YeW. The route doesn't seem to be getting properly set up, since post.html isn't getting loaded.
This isn't all that hard to do, but you need to have routing set up on your app. You can create this functionality in your existing app, or separate it into a new one, it's up to you. Here are the relevant things you'll need to include in your code:
In your app include ngRoute as a dependency:
var myApp = angular.module('myApp', ['ngRoute']);
Also include routing config for your app:
myApp.config(function($routeProvider){
$routeProvider
.when('/someroute', {
templateUrl: 'someFolder/withSomeFile.html'
}
.when('/someroutewithparamters/:aftercolonisparameter', {
templateUrl: 'someFolder/post.html'
}
});
You can include a default route as well, but it's not necessary if you'd rather not. Be sure to include angular-route.js in your index.html for this to work.
Now in your controller you can simply do something like:
myApp.controller('postCtrl', function($routeParams, $scope, postFactory){
$scope.post = postFactory.functionToLoadPost($routeParams.aftercolonisparameter);
});
Obviously this will be different for your implementation based on how everything is set up, and you'll probably want to pick better names for your elements than I did, but those are the things you'll need in place to make this work. It's actually pretty straightforward.
I'm trying to load a javascript file (using IRLibloader) after the Iron Router has rendered the template:
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
});
Router.route('/', {
name: 'landing',
template: 'landing',
onBeforeAction: function () {
var googleAPI = IRLibLoader.load('http://maps.googleapis.com/maps/api/js?libraries=places&sensor=false');
var fancyInput = IRLibLoader.load('/js/fancyInput.js');
var geoComplete;
if(googleAPI.ready()){
geoComplete = IRLibLoader.load('/js/jquery.geocomplete.min.js');
}
if(googleAPI.ready() &&
fancyInput.ready() &&
geoComplete.ready()){
console.log('All ready');
this.next(); // Render the page when all the libraries are ready
// Testing this here
if(Meteor.isClient){
console.log("Meteor.isClient");
IRLibLoader.load('/js/landing.js');
// Set places autocomplete
Template.landing.rendered = function(){
$('section :input').val('').fancyInput()[0].focus();
$('section :input').geocomplete();
console.log("loading.js ejecutandose (after render)");
}
}
}
}
});
But when I browse localhost:3000, the layout gets rendered, the googleAPI, fancyInput and geocomplete libraries are loaded too since the 'all ready' message gets printed at console, and landing.js also gets loaded (since it loads the background image and the message 'Meteor.isClient' also gets printed).
But then, the 'landing' template never gets rendered. Its content does not appear, and the console message inside the Template.landing.rendered never gets printed. This is the template.js file:
<template name="landing">
<img id='logo' src="img/logos/logo.png">
<div id='content'>
<section class='input'>
<div>
<input type='text' placeholder='Type text here'>
</div>
</section>
</div>
</template>
I also tried loading landing.js with onAfterAction, which seems to happen before the onBeforeAction according to the Firebug console. How strange!
I can't understand why the template is not being loaded, since no error appears at meteor console. Any idea?
EDIT: it does work if I remove the layout, which looks like this:
<template name="layout">
<head>
<title>Welcome to my app</title>
</head>
</template>
What's wrong with this layout?
So, I think you might be overthinking this a little. Why not use existing packages for these libraries? Aside from being significantly easier to use, some of that 3rd party code would get minified into the main app js file instead of making additional HTTP requests to download them.
For example, dburles:google-maps gets you the Google Maps API and extra libs of your choice (with the option to only load on specific routes) and jeremy:geocomplete gets you Geocomplete (which automatically installs that maps package as a dependency). See the jeremy:geocomplete README for implementation.
As for Fancy Input, why not create a simple Meteor package wrapper for that so you can just meteor add fancy-input?
Also, your Template.landing.rendered callback should not be in an onBeforeAction. Ideally, it should be in its own file with other code for the landing template.
In my Angular application I have a main view that changes according to the route, and then a sidebar that is displayed no matter what. My HTML looks like this:
<div id="container">
<div id="sidebar" ng-controller="SidebarCtrl">(sidebar code)</div>
<div ng-view id="content">Loading...</div>
</div>
And the javascript is loaded in this order:
Vendor.js (angular, etc)
App.js (my application)
The SidebarCtrl is defined in App.js. However right when Angular loads it sees the ng-controller directive and tries to assign it to the sidebar right off the bat, leading to a "argument SidebarCtrl is not a function" error unless app.js is cached. I was thinking of having the sidebar in a separate file and loading it before Vendor.js, but the sidebar is dependent on some services defined in App.js (namely, my custom auth service):
MyApp.controller('SidebarCtrl',
['$scope', '$location', 'auth', function($scope, $location, auth) {
(...)
}])
So either way I'm at a loss.
I'm sure there is a better way of setting this up...any ideas? Can I define SidebarCtrl as a simple function but still have access to the auth service?
Thanks
You can dynamically add the ng-controller attribute to the sidebar when your app loads:
var $self = angular.element('.sidebar');
$self.attr('ng-controller', window.USER_LOGGED_IN ? 'sidebarCtrl' : 'noUserCtrl');
angular.bootstrap(angular.element('body'));
This method is useful when the controller to be used depends on the application state. In your case, angular.bootstrap should do the trick.
Source
I'm trying to dynamically include a template into my index.html. The general structure of index.html is:
<body>
<header ng-controller="Main">
<section>
<!-- global stuff -->
</section>
<section ng-include="moduleName + '/views/menubar.html'">
<!-- module-based stuff -->
</section>
</header>
<div id="view" ng-view></div>
</body>
Sample URL
example.com/<app_name>/index.html#/<module_name>[/method_name]
I can't figure out how to update $scope.moduleName when the route changes. My trouble is two-fold:
The header's controller is Main, not the controller associated with the view, so I can't? update $scope.moduleName from the view's controller (because Main and the view's controller are siblings).
In Main, I tried setting a $scope.$on('$routeChangeSuccess',…), but apparently it is not notified of route changes.
I've thought of setting up a $rootScope.$on listener (as described in SO#15355346) for the route change and broadcasting down to children, who then emit back up their route, which is broadcasted back down so it is available to Main. But that seems heinous.
And I would really prefer to keep the header outside of ng-view.
EDIT I noticed that $route.current.scope has an object named with module_name (possibly because the name of the controller associated with the route's module_name is the same). I'm wondering if I might be able to somehow use the name of that object…
It's hard to say what's wrong in your code without the full picture. Things you show look fine to me.
Please see this plunk I've created to display the ability to do it. Take note that you also can extend route objects with custom properties, like moduleName here:
$routeProvider.when('/page1', {
template: 'one',
controller: 'one',
moduleName: 'firstModule'
});