Which is the better way to provide API in AngularJS library - angularjs

I am building a third-party library using AngularJS and I want to provide some API for user to interact with some internal properties/functions. Below are two solutions I'm considering, I want to know if some best practice for such a requirement.
Wrap the API in an scope object which can be accessed by the App.
angular.module('lib', []).directive('libDirective', function() {
return {
scope: {
api: '='
},
link: function(scope, elem, attrs) {
scope.api = {
sayHi: function() {
return 'Hi from lib';
}
};
}
};
});
angular.module('app', ['lib']).controller('AppCtrl', function($scope) {
// to do
});
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.5/angular.min.js"></script>
</head>
<body ng-app="app">
<div ng-controller="AppCtrl">
<lib-directive api="lib"></lib-directive>
{{lib.sayHi()}}
</div>
</body>
</html>
Wrap the API in a service which can be injected into the App module.
angular.module('lib', []).factory('libService', function() {
var hi = 'Hi from lib';
// return API
return {
sayHi: function() {
return hi;
}
};
});
angular.module('app', ['lib']).controller('AppCtrl', function($scope, libService) {
$scope.libHi = libService.sayHi();
});
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.5/angular.min.js"></script>
</head>
<body ng-app="app">
<div ng-controller="AppCtrl">
{{libHi}}
</div>
</body>
</html>

Related

Components in Angular 1.5.8 and 1.6.1

Here is my very simple code:
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.js"></script> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
</head>
<body>
<t-component test="test">
<div >
{{test}} John
</div>
</t-component>
<script>
angular.module('myApp', []).
component('tComponent', {
bindings: {
test: '='
},
controller: function() {
var self = this;
self.test='Hello';
}
});
</script>
</body>
</html>
I am getting Hello John only when using Angular 1.5.8. What do I have to do as to make the above to work with Angular 1.6.1? What am I missing?
I have this code working like that:
<t-component test="'test'"></t-component>
<script>
angular.module('myApp', []).
component('tComponent', {
template:'{{vm.test}} John',
bindings: {
test: '<'
},
controller: function() {
var self = this;
self.$onInit = function(){
// self.test ='hello'; //if you don`t want to use binding
}
},
controllerAs: 'vm'
});
</script>
<body>
<t-component test="'Hello'"></t-component>
<script>
angular.module('myApp', []).
component('tComponent', {
template: `
{{$ctrl.test}} John
`,
bindings: {
test: '='
},
controller: function() {
}
});
</script>
</body>
Then in controller you can change value of binding.
self.$onInit = function(){
self.test ='I am changed';
}

Google Signin button in AngularJS sometimes does not show up

I followed this link https://developers.google.com/identity/sign-in/web/sign-in to get Google Signin on Angular-based website.
I have seen some weird behavior. The signin button sometimes show but not always. When I refresh a page, only 1 in 5 refreshes, the button appears.
I tried in Chrome and Safari and both has same behavior.
Code:
index.html
<script src="https://apis.google.com/js/platform.js" async defer></script>
<meta name="google-signin-client_id" content="my_client_id">
login.html
<div class="g-signin2" data-onsuccess="onSignIn"></div>
login.js
angular.module('app').controller('LoginCtrl', function($scope) {
window.onSignIn = function(googleUser) {
// Get some info
}
});
My guess is that the platform.js script waits for the DOM-ready event and then looks for your div with the "g-signin2"-class. Though, in Angularjs things work a little different. The reason that it works sometimes, is because sometimes your div has been rendered by Angular and sometimes is hasn't been rendered before the Dom-ready event.
There's documentation on how to get the same result with your own javascript.
I made an example that follows your routing.
<!doctype html>
<html lang="en" ng-app="app">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div ng-view></div>
<script src="https://apis.google.com/js/platform.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular.min.js"></script>
<script src="https://code.angularjs.org/1.4.6/angular-route.min.js"></script>
<script>
angular.module('app',['ngRoute'])
.config(['$routeProvider',function($routeProvider){
$routeProvider
.when('/log-in', {
template: '<button ng-click="logInCtrl.onLogInButtonClick()">Log In</button>',
controller: 'LogInController',
controllerAs: 'logInCtrl'
}).otherwise({
redirectTo:'/log-in'
});
}])
.controller('LogInController',function(){
var self = this; //to be able to reference to it in a callback, you could use $scope instead
gapi.load('auth2', function() {//load in the auth2 api's, without it gapi.auth2 will be undefined
gapi.auth2.init(
{
client_id: 'CLIENT_ID.apps.googleusercontent.com'
}
);
var GoogleAuth = gapi.auth2.getAuthInstance();//get's a GoogleAuth instance with your client-id, needs to be called after gapi.auth2.init
self.onLogInButtonClick=function(){//add a function to the controller so ng-click can bind to it
GoogleAuth.signIn().then(function(response){//request to sign in
console.log(response);
});
};
});
});
</script>
</body>
</html>
Or as a directive:
<!doctype html>
<html lang="en" ng-app="app" ng-controller="MainController">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<google-sign-in-button on-sign-in="onSignIn(response)" g-client-id="CLIENTID.apps.googleusercontent.com"></google-sign-in-button>
<script src="https://apis.google.com/js/platform.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular.min.js"></script>
<script>
angular.module('app',[])
.controller('MainController',['$scope', function ($scope) {
$scope.onSignIn=function(response){
console.log(response);
}
}])
.directive('googleSignInButton',function(){
return {
scope:{
gClientId:'#',
callback: '&onSignIn'
},
template: '<button ng-click="onSignInButtonClick()">Sign in</button>',
controller: ['$scope','$attrs',function($scope, $attrs){
gapi.load('auth2', function() {//load in the auth2 api's, without it gapi.auth2 will be undefined
gapi.auth2.init(
{
client_id: $attrs.gClientId
}
);
var GoogleAuth = gapi.auth2.getAuthInstance();//get's a GoogleAuth instance with your client-id, needs to be called after gapi.auth2.init
$scope.onSignInButtonClick=function(){//add a function to the controller so ng-click can bind to it
GoogleAuth.signIn().then(function(response){//request to sign in
$scope.callback({response:response});
});
};
});
}]
};
});
</script>
</body>
</html>
After writing the previous examples I found a better and easier way to implement it. With this code you inherit the same button as you normally would.
<!doctype html>
<html lang="en" ng-app="app" ng-controller="MainController">
<head>
<meta charset="UTF-8">
<title>Document</title>
<meta name="google-signin-client_id" content="CLIENTID.apps.googleusercontent.com">
</head>
<body>
<google-sign-in-button button-id="uniqueid" options="options"></google-sign-in-button>
<script src="https://apis.google.com/js/platform.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular.min.js"></script>
<script>
angular.module('app', [])
.controller('MainController', ['$scope',
function($scope) {
//for more options visit https://developers.google.com/identity/sign-in/web/reference#gapisignin2renderwzxhzdk114idwzxhzdk115_wzxhzdk116optionswzxhzdk117
$scope.options = {
'onsuccess': function(response) {
console.log(response);
}
}
}
])
.directive('googleSignInButton', function() {
return {
scope: {
buttonId: '#',
options: '&'
},
template: '<div></div>',
link: function(scope, element, attrs) {
var div = element.find('div')[0];
div.id = attrs.buttonId;
gapi.signin2.render(div.id, scope.options()); //render a google button, first argument is an id, second options
}
};
});
</script>
</body>
</html>
I found another way to solve the problem a liitle bit simply.
The explanation from #sniel is perfect but I will let you know more simple solution.
you can use below sample code very simiraliry with using $watch
https://developers.google.com/identity/sign-in/web/build-button
<!-- html part -->
<div id="signInButton"></div>
//gapi is Asynchronously loaded so you need to watch
$scope.$watch('gapi', function(newValue,oldVale){
if(newValue !== oldVale){
if(gapi){
gapi.signin2.render('signInButton',
{
'onsuccess': $scope.onSuccess,
'onfailure': $scope.onFailure,
'scope':'https://www.googleapis.com/auth/plus.login'
}
);
}
}
});

AngularJS - how to pass object (created on the fly) with a interpolated data to a custom directive

I would like to create a custom directive to which I can pass an object (created on the fly) with interpolated values.
Example:
Newly created array as a parameter:
{userName: vm.data.name}
vm.data.name should be replaced with controller data.
Here is my code:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Custom directive</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.1/angular.min.js"></script>
<script>
(function(angular) {
'use strict';
angular.module('testDirectiveApp', [])
.controller('testController', ['$scope', function($scope) {
var data = {
name: 'ABC'
}
var vm = this;
vm.data = data;
}])
.directive('customDirective', ['$interpolate', function($interpolate) {
return {
scope: {
customParam: '='
},
link: function(scope, element, attributes){
scope.$watch('customParam', function() {
console.log(attributes.customParam, $interpolate(attributes.customParam)(scope), scope.$eval(attributes.customParam));
});
}
};
}]);
})(window.angular);
</script>
</head>
<body ng-app="testDirectiveApp">
<div ng-controller="testController as vm">
Name: <input ng-model="vm.data.name"> <hr/>
<div custom-directive custom-param="{userName: vm.data.name}"></div>
<p>{{vm.data.name}}</p>
</div>
</body>
</html>
Is this doable? What I'm doing wrong?
Best Regards,
The problem is that you are trying to access a scope variable with the attributes object, and not the scope object.
In your directive, change attributes.customParam to scope.customParam
http://plnkr.co/edit/1o4746GamtIcwHekoKyl?p=info

$compile() does not fire in custom Angular directives

I have the following directive:
angular.module("example_module", [])
.directive("example_directive", function() {
return {
compile: function(element) {
element.html('{{example}}');
return function($scope) {
$scope.example = "Hello world";
};
}
};
});
and the following HTML code:
<!DOCTYPE html>
<html ng-app="example_module">
<head>
<meta charset="utf-8">
<title>Example title</title>
<script src="lib/angular/angular.min.js"></script>
<script src="js/example.js"></script>
</head>
<body>
<div example_directive></div>
</body>
</html>
I would expect the directive to compile to Hello world, but it compile to an empty string instead. Where is the error?
I could use template or a link function, but my goal here is to understand how the compile function works.
This has to do with how angular handles directive names. I've changed your example to match angular's naming conventions and put together a Plunk
angular.module("example_module", [])
.directive("exampleDirective", function() {
return {
compile: function(element) {
element.html('{{example}}');
return function($scope) {
$scope.example = "Hello world";
};
}
};
});
<body>
<div example-directive></div>
</body>

Watch not working when HTML loaded from Directive in AngularJS

I am loading a partial in Angular dependant on the route of the URL.
When I load the partial it loads, and responds to the Controllers functions. However I have a directive which has a watcher. This does not work when I use the
It works fine when I load the HTML inside the main page. I have a Plunker of this here
http://plnkr.co/edit/DK33pIrp0HyhUOjwm5X2?p=preview
Essentially clicking "hello" should change the $scope.origin and the watcher should then fire its event. It does not.
My HTML:
<!DOCTYPE html>
<html ng-app="App">
<head lang="en">
<meta charset="utf-8">
<title>Custom Plunker</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
<link rel="stylesheet" href="style.css">
<script>
document.write('<base href="' + document.location + '" />');
</script>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/coffee-script/1.1.2/coffee-script.min.js"></script>
<script src="app.js"></script>
<script src="directive.js"></script>
</head>
<body ng-controller="MapCtrl">
<ng-view></ng-view>
</body>
</html>
app.js
var app;
app = angular.module("App", []);
app.config(function($routeProvider) {
return $routeProvider.when("/", {
templateUrl: "home.html",
controller: MapCtrl
});
});
this.MapCtrl = function($scope) {
return $scope.clicked = function() {
console.log("clicked");
$scope.origin = Math.floor(Math.random() * 11);
return console.log($scope.origin);
};
};
directive.js
(function(angular) {
var app;
app = angular.module("App");
return app.directive("leaflet", function() {
return {
restrict: "E",
replace: true,
transclude: true,
template: "<section id='map' class='map'></section>",
scope: {
origin: "=origin"
},
controller: function($scope, $attrs) {
return $scope.$watch("origin", (function(newValue, oldValue) {
return alert("its changed");
}), true);
}
};
});
})(angular);
home.html
<button ng-click="clicked()">hello</button>
how can I get this working?
edit: I have just made this pure JS and not coffeescript.
Thanks so all who helped me find the issue.
This can be done by setting the
<button ng-click="clicked()">hello</button>
to
<button ng-click="$parent.clicked()">hello</button>
This is because the ng-view will be a child. This simple fix is now working.

Resources