AngularJS directive with ng-repeat not rendering - angularjs

The problem is that I have to manage a list of gum balls that is retrieved from a service. The directive I've created seems to work when I hardcode the elements in the HTML, but when I attempt to dynamically allocate the gum balls using a ng-repeat.
HTML
<div ng-controller="GumballsCtrl">
<h1>Working</h1>
<ul>
<li ng-repeat="gumball in Gumballs">
<div class="gumballColor{{gumball.color}}">{{gumball.color}}</div>
</li>
</ul>
<h1>Problem - Expecting the same result at the work version</h1>
<ul>
<li ng-repeat="gumball in Gumballs">
<mygumball id={{gumball.id}} color="{{gumball.color}}">{{gumball.color}}</mygumball>
</li>
</ul>
</div>
JavaScript
var myApp = angular.module('myApp', []);
function GumballsCtrl($scope, Gumballs) {
$scope.Gumballs = Gumballs;
}
myApp.factory('Gumballs', function () {
return [{
id: '1',
color: 'R'
}, {
id: '2',
color: 'G'
}, {
id: '3',
color: 'B'
}, {
id: '4',
color: 'Y'
}, {
id: '5',
color: 'G'
}];
});
myApp.directive('mygumball', function ($scope) {
return {
restrict: 'E',
scope: {},
link: function (scope, element, attrs) {
if (attrs.color !== '' && attrs.color !== undefined) {
scope.color = attrs.color;
} else {
scope.color = 'U';
}
},
replace: true,
template: "<div class='gumballColor{{color}}'>{{color}}</div>"
};
});
CSS
.gumballColorR {
font-size: 12px;
text-align: center;
padding: 2px;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
border: solid 1px #CC0000;
background-color: #FF0000;
width: 15px;
height: 15px;
margin-left: 5px;
margin-top: 5px;
}
.gumballColorG {
font-size: 12px;
text-align: center;
padding: 2px;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
border: solid 1px #00CC00;
background-color: #00FF00;
width: 15px;
height: 15px;
margin-left: 5px;
margin-top: 5px;
}
.gumballColorB {
font-size: 12px;
text-align: center;
padding: 2px;
color: #FFFFFF;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
border: solid 1px #0000CC;
background-color: #0000FF;
width: 15px;
height: 15px;
margin-left: 5px;
margin-top: 5px;
}
.gumballColorY {
font-size: 12px;
text-align: center;
padding: 2px;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
border: solid 1px #CCCC00;
background-color: #FFFF00;
width: 15px;
height: 15px;
margin-left: 5px;
margin-top: 5px;
}
.gumballColorU {
font-size: 12px;
text-align: center;
padding: 2px;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
border: solid 1px #CCCCCC;
background-color: #DDDDDD;
width: 15px;
height: 15px;
margin-left: 5px;
margin-top: 5px;
}
http://jsfiddle.net/i3sik/NGB9v/22/
The id and color attributes when passed into the directive end up being undefined when passed using the ng-repeat, but work when hardcoded in the HTML.

The problem here is your isolate scope. By using scope: {} you created a new, isolate scope to act on that element. Isolate scopes don't inherit from the parent scope. All attributes and content on directives with isolate scopes are evaluated within the context of the isolate scope. gumball doesn't exist in the isolate scope, so everything comes up as undefined.
You have two choices to fix this: (1) remove the isolate scope (e.g. scope: true to create a child scope); or (2) bind the values in your isolate scope.
To bind your attributes to scope variables, you simply need to specify the scope and the kind of binding you want:
scope: {
id: '#',
color: '#'
},
This says that the attributes id and color are to be interpolated in the context of the parent scope and then added to the scope. You can remove all that logic inside your link function - this will do it for you.
But this still leaves the problem of the content inside the directive. To interpolate that in the context of the parent scope, you need transclusion:
transclude: true,
template: "<div class='gumballColor{{color}}' ng-transclude></div>"
Transclusion takes the contents of the element and interpolates relative to a new child of the parent scope, e.g. where gumball would still be defined.
With these two changes, your directive will work as desired.
If you're confused about which scope to use, here's another SO question that might help: When writing a directive, how do I decide if a need no new scope, a new child scope, or a new isolate scope?
Side note: Even without the isolate scope, the logic in your link function to determine attribute values wouldn't work. Order of execution is the important part here, which is roughly: compiler -> controller -> link -> interpolation. Until the interpolation is done, there is no value for your attributes. So your checks won't work.
That said, you can set up an $observe on interpolated attributes; the $observe will always fire the first time, even if there was no value passed. You can use this to set your default. $observe is also very efficient.
attrs.$observe( 'attr1', function(val) {
if ( !angular.isDefined( val ) ) {
scope.attr1 = 'defaultValue';
}
});

Related

initializing angular after DOM has changed, doesn't work

I'm trying to re-initialize my angular app, so it will re-render the DOM since I injected elements after loading.
This is the short version of my angular function:
JS
(function(angular) {
'use strict';
angular.module('myApp', ['ui.bootstrap'])
.controller('Controller', function($scope, $http) {
$scope.highlighter1 = function(side, string, load) {
highlighter.deserialize(string);
};
$http.get('/comments')
.success(function(response) {
$scope.comments = response;
var allEl=[];
var i;
for (i=0; i<response.length; i++) {
allEl.push(response[i]._id);
}
$http.post('/ranges', {"commentIds":allEl})
.success(function(result){
result.forEach(function(item){
$scope.highlighter1(item.dataAction, item.rangyObject, true);
})
})
});
angular.bootstrap(document.getElementsByTagName('span'),['myApp']);
})
})(window.angular);
my 's are injected when I run the deserializing of the highlighter object. I'm also assigning them attributes of uib-popover, so in the end I have 's in my documents with these attributes, and I'm re-rendering them when calling on angular.bootstrap again on them.
They are being rendered (when I try to manually call it again from the console it says that it fails because these elements are already rendered), but the pop-over still doesn't do what it's supposed to do (when I click it doesn't popover).
CSS
#import url(http://fonts.googleapis.com/css?family=Roboto:400,300);
body {
color: #595959;
font-family: "Roboto", sans-serif;
font-size: 16px;
font-weight: 300;
line-height: 1.5;
}
.highlight-a {
display: inline-block;
background-color: #99c2ff;
border: 1px solid #7699d1;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
color: #223f7a;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
border: 1px solid #e1e1e8;
}
HTML
<div ng-repeat="i in comments">
<div id={{i._id}} class="task" commentId={{i._id}}> {{i.text}} </div>
</div>

Why the message can't be pushed in the array?

I'm using AngularJS v1.6.0-rc.2. I am making a modal-like chat box and I've found a major problem. I wonder why the message can't be pushed into an array when I clicked on a button 'Send' like this. I assume that the message is sent as "sender".
View
<html>
<head>
<!-- I've included the script, CSS file, and fonts -->
</head>
<body ng-app='ModalDemo'>
<div ng-controller='MyCtrl'>
<b ng-click='toggleModal()'><label>username</label></b>
<modal-dialog show='modalShown' width='250px' height='370px'>
<name>
<span>username</span>
</name><hr>
<content>
<div width=100% ng-repeat='message in messages'>
<div ng-if='message.u==1'><span class='sender'>{{message.m}}</span></div>
<div ng-if='message.u==0'><span class='receiver'>{{message.m}}</span></div>
</div><span class='sender'>{{textmsg}}</span>
</content>
</modal-dialog>
</div>
</body>
</html>
Model & Controller
<script>
var app = angular.module('ModalDemo',[]);
app.directive('modalDialog', function() {
return {
scope: {
show: true;
},
replace: true,
transclude: {
'name': '?name',
'content': '?content'
},
link: function(scope, element, attrs) {
scope.dialogStyle = {};
if (attrs.width)
scope.dialogStyle.width = attrs.width;
if (attrs.height)
scope.dialogStyle.height = attrs.height;
scope.hideModal = function() {
scope.show = false;
};
},
template: "<div class='ng-modal' ng-show='show'>\n\
<div class='ng-modal-overlay' ng-click='hideModal()'></div>\n\
<div class='ng-modal-dialog' ng-style='dialogStyle'>\n\
<div class='ng-modal-name' ng-transclude='name'></div>\n\
<div class='ng-modal-close' ng-click='hideModal()'>_</div>\n\
<div class='ng-modal-dialog-content' ng-transclude='content'></div>\n\
<div class='ng-modal-textbox'>\n\
<input class='textinput' ng-model='textmsg'>
<button ng-click='send()' class='send'>Send</button>\n\
</div></div></div>"
};
});
app.controller('MyCtrl', ['$scope', function($scope) {
$scope.modalShown = false;
$scope.toggleModal = function() {
$scope.modalShown = !$scope.modalShown;
};
$scope.messages=[
{'m':'hi','u':1},
{'m':'hello', 'u':1},
{'m':'is it username?', 'u':1},
{'m':'yeah!','u':0}];
$scope.send = function(){
$scope.messages.push(
{'m':$scope.textmsg, 'u':1 }
);
$scope.textmsg="";
};
}]);
</script>
Here is my CSS file if needed.
CSS
.ng-modal-overlay {
position:absolute;
z-index:9999;
top:0;
left:0;
width:100%;
height:100%;
background-color:#000000;
opacity: 0.0;
}
.ng-modal-dialog {
z-index:10000;
position: fixed;
width: 50%;
bottom: 0px;
left: 75%;
box-shadow: 1px 1px 2px #000000;
background-color: #fff;
}
.ng-modal-dialog-content {
text-align: left;
overflow-y: scroll;
height: 300px;
width:100%;
}
.ng-modal-close {
position: absolute;
top: 3px;
right: 5px;
padding: 3px 6px;
cursor: pointer;
font-size: 120%;
display: inline-block;
font-weight: bold;
font-family: 'arial', 'sans-serif';
color: white;
}
.ng-modal-name{
background:#4A86E8;
padding:10px;
color: white;
}
.ng-modal-textbox{
width:100%;
height: 25px;
border-top: 1px solid black;
bottom:0px;
position:absolute;
}
.send{
background: #4a86e8;
border: 0;
font-size: 1em;
color: white;
float: right;
height:inherit;
font-family: 'Montserrat';
}
.textinput{
width: 185px;
font-size: 1em;
border: 0;
padding-left: 3px;
}
.sender{
float: right;
margin:8px;
padding: 5px 8px;
border-radius: 6px;
font-family: arial;
background: #8eb5f2;
box-shadow: 1px;
color: white;
font-size: 0.8em;
box-shadow: 1px 1px 6px #000;
max-width: 60%;
}
.receiver{
float:left;
margin:8px;
padding: 5px 8px;
border-radius: 6px;
font-family: arial;
box-shadow: 1px;
color: black;
font-size: 0.8em;
background: #eaebed;
box-shadow: -1px 1px 6px #000;
max-width: 60%;
}
Here is his working PLUNKER for his problem
I have fixed all the errors but ever thing is working fine.
Every thing goes fine,
You missed a lot of brackets here that messed your code.
return {
scope: {
show: true,
replace: true,
transclude: {
'name': '?name',
'content': '?content'
},
link: function(scope, element, attrs) {
scope.dialogStyle = {};
if (attrs.width)
scope.dialogStyle.width = attrs.width;
if (attrs.height)
scope.dialogStyle.height = attrs.height;
scope.hideModal = function() {
scope.show = false;
};
},
Also your push
var obj= {
'm':$scope.textmsg,
'u':1
}
$scope.messages.push(obj);
Please tell me one thing which the place your clicking to toggle in this HTML, Also do you think it is clickable?
<b ng-click="toggleModal()">
<label>username</label>
</b>

angular directive's content not rendered, unless you force page reflow manually

I experience the following strange behavior in ui-bootstrap and angular 1.4. When I put a footable table directive inside a customized bootstrap panel, called hpanel, the footable initially takes more place than the panel itself:
But if I resize the screen (e.g. by collapsing the Developer Tools panel here), the footable directive draws itself and fits within panel:
Importantly, I've experienced similar problems with angular-c3 charts directives (they load incorrectly, exceeding the size of hpanel, but upon page resize behave fine), so it's probably not just a broken directive.
Have you seen anything similar?
DETAILS:
Below is an HTML template that represents the non-functional part of page. There we have an hpanel and within it a table with angular-footable directive ^1.0.3, applied to it.
Here's the template (toolList.html):
<div class="content">
<div class="row">
<div class="col-lg-12">
<div class="hpanel">
<div class="panel-heading">
<div class="panel-tools">
<a class="showhide"><i class="fa fa-chevron-up"></i></a>
<a class="closebox"><i class="fa fa-times"></i></a>
</div>
Available tools.
</div>
<div class="panel-body">
<input type="text" class="form-control input-sm m-b-md" id="filter" placeholder="Search in table">
<table id="example1" class="footable table table-stripped toggle-arrow-tiny" data-page-size="8" data-filter=#filter>
<thead>
<tr>
<th data-toggle="true">Id</th>
<th>Class</th>
<th>Label</th>
<th>Description</th>
<th data-hide="all">Owner</th>
<th data-hide="all">Contributor</th>
<th data-hide="all">Inputs</th>
<th data-hide="all">Outputs</th>
<th data-hide="all">Base command</th>
<th data-hide="all">Arguments</th>
<th data-hide="all">Requirements</th>
<th data-hide="all">Hints</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="tool in vm.tools">
<td><a ui-sref="tool-detail({id: tool.id})">{{tool.id}}</a></td>
<td>{{tool.tool_class}}</td>
<td>{{tool.label}}</td>
<td>{{tool.description}}</td>
<td>{{tool.owner}}</td>
<td>{{tool.contributor}}</td>
<td>{{tool.baseCommand}}</td>
<td>{{tool.arguments}}</td>
<td>{{tool.requirements}}</td>
<td>{{tool.hints}}</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5">
<ul class="pagination pull-right"></ul>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
</div>
The footable directive is meant to hide some columns of the table and show them upon click on a table row. It also provides pagination. It doesn't seem to work upon page load, but when I resize the page and the size of screen crosses the media-type margin (so that from medium-size screen it becomes large screen in bootstrap css terms), pagination buttons appear and columns that are meant to be hidden are hidden.
Here's how I import the footable directive in my main module app.js:
require("footable/js/footable");
require("footable/js/footable.filter");
require("footable/js/footable.striping");
require("footable/js/footable.sort");
require("footable/js/footable.paginate");
require("footable/css/footable.core.css")
require("angular-footable");
angular.module("app", [
...,
"ui.footable",
])
I use webpack to load all those modules and bower to install the dependencies.
hpanel is just a scss class, here is its definition:
/* Panels */
.hpanel > .panel-heading {
color: inherit;
font-weight: 600;
padding: 10px 4px;
transition: all .3s;
border: 1px solid transparent;
}
.hpanel .hbuilt.panel-heading {
border-bottom: none;
}
.hpanel > .panel-footer, .hpanel > .panel-section {
color: inherit;
border: 1px solid $border-color;
border-top: none;
font-size: 90%;
background: $color-bright;
padding: 10px 15px;
}
.hpanel.panel-collapse > .panel-heading, .hpanel .hbuilt {
background: #fff;
border-color: $border-color;
border: 1px solid $border-color;
padding: 10px 10px;
border-radius: 2px;
}
.hpanel .panel-body {
background: #fff;
border: 1px solid $border-color;
border-radius: 2px;
padding: 20px;
position: relative;
}
.hpanel.panel-group .panel-body:first-child {
border-top: 1px solid $border-color;
}
.hpanel.panel-group .panel-body {
border-top: none;
}
.panel-collapse .panel-body {
border: none;
}
.hpanel {
background-color: none;
border: none;
box-shadow: none;
margin-bottom: 25px;
}
.panel-tools {
display: inline-block;
float: right;
margin-top: 0;
padding: 0;
position: relative;
}
.hpanel .alert {
margin-bottom: 0;
border-radius: 0;
border: 1px solid $border-color;
border-bottom: none;
}
.panel-tools a {
margin-left: 5px;
color: lighten($color-text, 20%);
cursor: pointer;
}
.hpanel.hgreen .panel-body {
border-top: 2px solid $color-green;
}
.hpanel.hblue .panel-body {
border-top: 2px solid $color-blue;
}
.hpanel.hyellow .panel-body {
border-top: 2px solid $color-yellow;
}
.hpanel.hviolet .panel-body {
border-top: 2px solid $color-violet;
}
.hpanel.horange .panel-body {
border-top: 2px solid $color-orange;
}
.hpanel.hred .panel-body {
border-top: 2px solid $color-red;
}
.hpanel.hreddeep .panel-body {
border-top: 2px solid $color-red-deep;
}
.hpanel.hnavyblue .panel-body {
border-top: 2px solid $color-navy-blue;
}
.hpanel.hbggreen .panel-body {
background: $color-green;
color: #fff;
border:none;
}
.hpanel.hbgblue .panel-body {
background: $color-blue;
color: #fff;
border:none;
}
.hpanel.hbgyellow .panel-body {
background: $color-yellow;
color: #fff;
border:none;
}
.hpanel.hbgviolet .panel-body {
background: $color-violet;
color: #fff;
border:none;
}
.hpanel.hbgorange .panel-body {
background: $color-orange;
color: #fff;
border:none;
}
.hpanel.hbgred .panel-body {
background: $color-red;
color: #fff;
border:none;
}
.hpanel.hbgreddeep .panel-body {
background: $color-red-deep;
color: #fff;
border:none;
}
.hpanel.hbgnavyblue .panel-body {
background: $color-navy-blue;
color: #fff;
border:none;
}
.panel-group .panel-heading {
background-color: $color-bright;
}
.small-header .hpanel {
margin-bottom: 0;
}
.small-header {
padding: 0 !important;
}
.small-header .panel-body {
padding: 15px 25px;
border-right: none;
border-left: none;
border-top: none;
border-radius: 0;
// background: $color-bright;
}
.panel-body h5, .panel-body h4 {
font-weight: 600;
}
.small-header .panel-body h2 {
font-size: 14px;
font-weight: 600;
text-transform: uppercase;
margin: 0 0 0 0;
}
.small-header .panel-body small {
color: lighten($color-text, 10%);
}
.hbreadcrumb {
padding: 2px 0px;
margin-top: 6px;
margin-bottom: 0px;
list-style: none;
background-color: #fff;
font-size: 11px;
> li {
display: inline-block;
+ li:before {
padding: 0 5px;
color: $color-navy-blue;
}
}
> .active {
color: lighten($color-text,20%);
}
}
.wrapper {
padding: 10px 20px;
}
.hpanel.collapsed .panel-body, .hpanel.collapsed .panel-footer {
display: none;
}
.hpanel.collapsed .fa.fa-chevron-up:before {
content: "\f078";
}
.hpanel.collapsed .fa.fa-chevron-down:before {
content: "\f077";
}
.hpanel.collapsed.panel-collapse .panel-body {
border-width: 0 1px 1px 1px;
border-color: $border-color;
border-style: solid;
}
.hpanel.collapsed .hbuilt.panel-heading {
border-bottom: 1px solid $border-color;
}
body.fullscreen-panel-mode {
overflow-y: hidden;
}
.hpanel.fullscreen {
z-index: 2030;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: auto;
margin-bottom: 0;
}
.hpanel.fullscreen .showhide {
display: none;
}
.hpanel.fullscreen .panel-body {
min-height: calc(100% - 77px);
}
Here's tool.module.js file, which animates the template:
import angular from "angular";
var ToolResource = require("workflow/tool/tool.service");
class ToolListController {
// #ngInject
constructor($location, $stateParams, $state, tools) {
this.$location = $location;
this.$state = $state;
this.$stateParams = $stateParams;
this.tools = tools;
}
}
// #ngInject
function routesList($stateProvider) {
$stateProvider.state("tool-list", {
url: "/tool",
parent: "layout",
templateUrl: "/app/workflow/tool/toolList.html",
controller: "ToolListController",
controllerAs: "vm",
data: {
pageTitle: "Tool",
pareDesc: "List of tools, available for workflow construction.",
},
resolve: {
ToolResource: "ToolResource",
tools: function(ToolResource) {
return ToolResource.query().$promise;
}
}
});
}
module.exports = angular.module("tool", [])
.service('ToolResource', ToolResource)
.controller('ToolListController', ToolListController)
.config(routesList);
tool.service.js:
module.exports = function ToolResource($resource) {
return $resource('/api/tool/:id', {id: '#id'});
}
ANSWER:
Community is awesome!
1.5 years ago this directive was created
12 days ago this bug was fixed by Alexryan in his fork
10 days ago I posted this question on StackOverflow
8 days ago I placed a bounty on this question
7 days ago ziscloud approved pull request
in the morning today the bounty expired and in the nick of time Walfrat found out that the bug was fixed
So, yes, it was a bug in the directive that made it draw itself before getting the data from server. With the bugfix I just added load-when="vm.tools" attribute to the directive and it works fine now.
Thank you, Alexryan, ziscloud, Walfrat and other commenters/answerers. StackOverflow and Github just made my day!
Are you using this directive ? https://github.com/ziscloud/angular-footable/blob/master/src/angular-footable.js. It's an homemade (meaning not done by the editor of the footable) directive so it can be not rightly implemented to works with Angularjs.
Looking at the code it seems that you have to use an attribute load-when if you want to delay the initialization of the grid even though you use the resolve attribute in your state, it can be worth to test it.load-when shall be an empty array at start an will trigger the load after the array won't be empty anymore, but the data binded won't be used for the initialization from what i saw.
Note : i wasn't able to set a proper plnkr myself, i don't know the version you're using (and with which jQuery version) and online links doesn't seems available.
Since you are asynchronously loading data (as was mentioned in the comments) your html is rendered prior to it having any data in it. This means the directive may be fired too early (if it is attempting to adapt based on data). Typically, in this scenario, you'll want to throw an ng-if on the portion of your html that is dependent on the data loading (and show a loading gif or something in its place). You can either run the ng-if off of the data itself being defined, or maintain a separate boolean that you set once the promise is resolved.

How change css :after in Angular JS?

I have HTML block:
<div class="item" ng-click="toogle()"></div>
And CSS style:
.item:after {
content: "\A";
border-style: solid;
border-width: 8px 8px 0 8px;
border-color: #7da9cd transparent transparent transparent;
position: absolute;
left: 50%;
bottom: -8px;
margin-left: -12px;
}
How I can hide style :after after each click toggle?
Fix your css, :after isn't ok, and then try something like:
CSS:
.item:{
display: block;
}
JS:
$scope.toggle = function(){
angular.element('.item').css('display','none');
}

How to Avoid Jump Between Checked and Unchecked States

I have custom checkboxes, and the content is jumping when I click between checked and unchecked states. How can I stop this from happening? Here's my code:
CSS:
input[type=checkbox] {
display: none;
}
label:before {
content: "";
display: inline-block;
width: 16px;
height: 16px;
margin-right: 18px;
background-color: rgba(225, 225, 225);
border-radius:4px;
border:1px solid #cecece;
}
input[type=checkbox]:checked + label:before {
content: "\2713";
font-size: 15px;
color: red;
text-align: center;
line-height: 15px;
}
HTML:
<input id="checkbox1" type="checkbox" class="checkbox" name="lists[Fortune Daily]" />
<label for="checkbox1"><img class="list" src="http://email-media.s3.amazonaws.com/fortune/fortunef_55x50.png" /> <span>Fortune Daily</span>
</label>
Thank you in advance!
It's no problem to correct the jumping. See the below code:
input[type=checkbox] {
display: none;
}
label {
font-size: 15px;
vertical-align: top;
}
label:before {
content: "\2713";
display: inline-block;
width: 16px;
height: 16px;
margin-right: 18px;
background-color: rgba(225, 225, 225);
border-radius:4px;
border:1px solid #cecece;
font-size: 15px;
color: white;
text-align: center;
line-height: 15px;
}
input[type=checkbox]:checked + label:before {
color: red;
}
<input id="checkbox1" type="checkbox" class="checkbox" name="lists[Fortune Daily]" />
<label for="checkbox1"><img class="list" src="http://email-media.s3.amazonaws.com/fortune/fortunef_55x50.png" /> <span>Fortune Daily</span>
</label>
However, I wouldn't recommend you to continue with this solution, because it's seems to be impossible to make a correct vertical alignment of the elements here. For example, instead of label with 'before' you could realize it by an outside div with display:table property and three inside element with display:table-cell. At least you will have a full control on elements' placement not dependent on font size.

Resources