-
-
Save ryanflorence/6833014 to your computer and use it in GitHub Desktop.
// Because people can't seem to find the gist description, here is the source | |
// of this code, a post found in last weeks JS Weekly, it is not my code | |
// http://www.angularails.com/articles/creating_simple_directive_in_angular | |
angular.module('ui-multi-gravatar', []) | |
.directive('multiAvatar', ['md5', function (md5) { | |
return { | |
restrict: 'E', | |
link:function(scope, element, attrs) { | |
var facebookId = attrs.facebookId; | |
var githubUsername = attrs.githubUsername; | |
var email = attrs.email; | |
var tag = ''; | |
if ((facebookId !== null) && (facebookId !== undefined) && (facebookId !== '')) { | |
tag = '<img src="http://graph.facebook.com/' + facebookId + '/picture?width=200&height=200" class="img-responsive"/>' | |
} else if ((githubUsername !== null) && (githubUsername !== undefined) && (githubUsername !== '')){ | |
tag = '<img src="https://identicons.github.com/' + githubUsername + '.png" style="width:200px; height:200px" class="img-responsive"/>'; | |
} else { | |
var hash = "" | |
if ((email !== null) && (email !== undefined) && (email !== '')){ | |
var hash = md5.createHash(email.toLowerCase()); | |
} | |
var src = 'https://secure.gravatar.com/avatar/' + hash + '?s=200&d=mm' | |
tag = '<img src=' + src + ' class="img-responsive"/>' | |
} | |
element.append(tag); | |
} | |
}; | |
}]); |
App.XAvatarComponent = Ember.Component.extend({ | |
tagName: 'img', | |
classNames: 'img-responsive', | |
width: 200, | |
height: 200, | |
service: 'gravatar', | |
src: function() { | |
return this[this.get('service')+'Url'](); | |
}.property('service', 'user'), | |
facebookUrl: function() { | |
return 'http://graph.facebook.com/' + this.get('user') + '/picture?type=large'; | |
}, | |
githubUrl: function() { | |
return 'https://identicons.github.com/' + this.get('user') + '.png' | |
}, | |
gravatarUrl: function() { | |
var hash = md5.createHash(this.get('user').toLowerCase()); | |
return 'https://secure.gravatar.com/avatar/' + hash + '?s=200&d=mm'; | |
} | |
}); |
Preview - http://jsbin.com/ExamAxI/5
var app = angular.module("AngularApp", ['ui-multi-gravatar']);
angular.module('ui-multi-gravatar', [])
.directive('multiAvatar', ['imgService', function (services) {
return {
restrict: 'E',
replace: true,
template: "<img class='img-responsive' />",
link: function(scope, element, attrs) {
var serviceName = attrs.service || "gravatar";
var user = attrs.user;
var imgSrc = services[serviceName + 'Url'](user);
var height = attrs.height || 200;
var width = attrs.width || 200;
element.attr({
src: imgSrc,
height: height,
width: width
});
}
};
}])
.service('imgService', function() {
var facebookUrl = function(user) {
return 'http://graph.facebook.com/' + user + '/picture?type=large';
};
var githubUrl = function(user) {
return 'https://identicons.github.com/' + user + '.png';
};
var gravatarUrl = function(user) {
var hash = CryptoJS.MD5(user.toLowerCase());
return 'https://secure.gravatar.com/avatar/' + hash + '?s=200&d=mm';
};
var services = {
'facebookUrl' : facebookUrl,
'githubUrl': githubUrl,
'gravatarUrl': gravatarUrl
};
return services;
});
Something I didn't know before working on this, custom angular directives can't be self-closing, you really need to have a otherwise it can get confused and break. (starts doing weird nesting)
This example trolled me good, just spent the last few mins refactoring the angular for a closer comparison :)
Running here: http://jsbin.com/uhUCeLO/4/edit?html,js,output
Code:
var app = angular.module("AngularApp", ['ui-multi-gravatar']);
angular.module('ui-multi-gravatar', [])
.directive('multiAvatar', function (UserImages) {
return {
restrict: 'E',
replace: false,
scope: {
width: '@',
height: '@',
service: '@',
user: '@'
},
template: "<img class='img-responsive' width='{{width || 100}}' height='{{height || 100}}' ng-src='{{src}}' />",
link: function(scope, element, attrs) {
scope.src = UserImages[scope.service || "gravatar"](scope.user);
}
};
})
.service('UserImages', function() {
this.facebook = function(user) {
return 'http://graph.facebook.com/' + user + '/picture?type=large';
};
this.github = function(user) {
return 'https://identicons.github.com/' + user + '.png';
};
this.gravatar = function(user) {
var hash = CryptoJS.MD5(user.toLowerCase());
return 'https://secure.gravatar.com/avatar/' + hash + '?s=200&d=mm';
};
});
@rpflorence I think in the event of a MIA author, you'd want to use a decorator in order to do those same functionality changes in angular
Why has nobody rewritten my ember example yet? 🧌
var services = {
facebook: 'http://graph.facebook.com/$id/picture?type=large',
github: 'https://identicons.github.com/$id.png',
gravatar: 'https://secure.gravatar.com/avatar/$id?s=200&d=mm'
}
function getAvatarUrl (id, service) {
var url = services[service];
if (url && service != 'gravatar') {
return url.replace('$id', id);
} else {
url = services.gravatar;
id = md5.createHash(id.toLowerCase());
return url.replace('$id', id);
}
}
function makeAvatar (id, service, size) {
var img = document.createElement('img');
img.width = img.height = (size || 200);
img.src = getAvatarUrl(id, service);
return img;
}
@chee so the user changes their name while on the page, now what? In ember/angular its updated automatically.
@dcherman yes, if you're using a service, the original example was not.
@rpflorence Because you know what you're doing with Ember, but not with Angular? :)
@rpflorence seems unlikely for this example, but if they did then just bind to that event and update the image source - all this smoke and mirrors seems like overkill and not at all obvious
what about if the user deletes their github profile while they're on the page
Did you intentionally butcher the angular example here?
@iammerrick @bclinkinbeard Read the description of the gist?
@angus-c yeah, that's what I did with MooTools and then Backbone for several years. Bound templates + components is way better. And once you get over the declarative template hump, hopping into other's code and figuring out how it all works is ridiculously more obvious.
EMBER FTW!
JSBin: http://jsbin.com/ember-component/2/edit
App = Ember.Application.create();
App.XAvatarComponent = Ember.Component.extend({
tagName: 'img',
classNames: 'img-responsive',
attributeBindings: 'width height src'.w(),
width: 100,
height: 100,
service: 'gravatar',
src: function() {
return this[this.get('service')](this.get('user'));
}.property('service'),
facebook: function(user) {
return 'http://graph.facebook.com/' + user + '/picture?type=large';
},
github: function(user) {
return 'https://identicons.github.com/' + user + '.png';
},
gravatar: function(user) {
return 'https://secure.gravatar.com/avatar/' + CryptoJS.MD5(user.toLowerCase()) + '?s=200&d=mm';
}
});
I see it is not your code, and that you don't use Angular, which is why I am confused about why you'd try to provide a comparison. I can assure you if I wrote a feature in both Angular and Ember, the Ember code would be crap because I don't know/use it. Because of that, I simply wouldn't write the comparison.
@bclinkinbeard I have a pretty deep understanding of angular and have built a few (admittedly trivial) apps with it. And, again, I didn't write that angular code, it was taken wholesale from a popular blog post featured in JS Weekly.
@RyanHirsch @rpflorence It's not just Angular, '' is interpreted as '' by HTML5 browsers; the slash is stripped. That's why the above example is incorrect.
Thinking about this more (unfortunately), the problem with the original Angular code isn't really a lack of idiomatic-ness. Sure, accessing the attrs directly instead of properly defining a child scope is less than ideal, but the biggest problem is the multi level conditional spaghetti. Breaking that out into a set of clean *Url functions isn't an Angular or Ember best practice/idiom, it's just fundamental code organization.
If you took the original Ember code and removed the separate service parameter and stuffed the *Url functions into src and navigated them with nothing but conditionals it would be shitty code too. I realize the Angular code was apparently linked from a popular newsletter, but it's still fundamentally bad code IMO. Not bad Angular code, just plain old, regular bad code.
JS Weekly. LOL.
@bclinkinbeard for background, a friend said "how would you do that in ember" so I did it, made the gist and then the fun began. Here's my original tweet: https://twitter.com/ryanflorence/status/386241210337067009 I did not intend to say this is a valid or fair comparison, I was just answering a question for a friend. People take these things way too seriously. Both frameworks are great and arrive at nearly identical usage syntax <component attr=value>, {{#component attr=value}}
.
I think there is value in considering the APIs the two frameworks give you for people picking one.
Most angular APIs just give you a single function hook to fill in and possibly return from (controllers, link, module, service), so people tend to drop everything into it because angular doesn't prescribe how to structure the code inside the hook. Makes getting started super easy.
Ember requires you to extend its objects (controller, component, route), which means you provide an object literal to your new constructor definition, rather than fill in a function body and return from it, so people tend to write methods because they didn't have to decide how to structure their modules.
I won't claim one is better, I just know which one I prefer.
Nice gist! I know which one I choose/use :)
@rpflorence I'm pretty sure I'm the one that screwed you here. I tweeted this as a "comparison" of ember and angular. https://mobile.twitter.com/polotek/status/387601840516259841
I didn't read the gist description and screwed up the context for everybody. Sorry.
Consider the author of this library is MIA and github has changed their avatar url. Also, consider you want to add more services to this component. In Ember you would:
I'm not sure how that would work in angular, I have asked and been told "angular uses object composition, not inheritance". I don't know how to translate that into this use case.
Also note the use of
width
andheight
. You can pass those attributes in to override the defaults.