You are not thinking 'the Angular way' enough yet. :)
Timing issues
You create the directive content inside the link function, which means all your content has to be available at that time. As you have noticed, this is a wrong assumption. The content must be allowed to update when new data is available (i.e. when asynchronous operations complete).
The Angular way of manipulating the DOM is two-way bindings. The idiomatic way of displaying content in place of the directive is to write a template for the directive.
function myDirective() {
return {
...
template: '<div>{{someVar}}</div>',
...
};
}
Using this technique, the DOM will update every time content is updated (that's why we use Angular right?). Add a template to your transformAndOutput directive, and remove the elm.append line in the link function. Now, when the asynchronous callback updates content, the DOM will update to reflect the change.
DEMO
You will notice that in this new demo, both lines show The value of content as called by loadData2 is: 2.0.171.17, which is not what you expect. This brings us to a second issue.
Scoping
Directives generally should have their own, isolated scope, so that they don't 'dirty' the host scope, or cause conflict with other components. In the code you provided, your two directives both modify their host scope by reading and writing to scope.data and scope.callingFunction, so there is a conflict.
To make a directive have its own scope, use the scope option.
function myDirective() {
return {
...
scope: true,
...
};
}
The directive scope does not inherit the host scope. You can, however, import some data from the host scope. I will not detail that here since it is not needed to answer the question. Please refer to Angular's Guide to Directives.
On the other hand, a nested directive's scope does inherit from the scope of parent directives. This means the transformAndOutput directive can access the properties of the scope of its parent loadData. You should encode that transformAndOutput requires a parent loadData by adding a require: '^loadData' option to transformAndOutput (in the demo it is either loadData1 or loadData2 so I could not add this option).
Add scope: true to your loadData1 and loadData2 directives so that they each have their own content and callingFunction. The previous holder for those variables - the controller - can now be safely removed.
DEMO
A last minor thing: your directive should use HTML-like names (lower case + dashes) in the HTML and JS-like names (camel case) in JS. The conversion is automatically handled by Angular.
Final code
app.directive('loadData1', function($http) {
return {
restrict: 'E',
replace: true,
scope: true,
link: function(scope, elm, attrs) {
scope.callingFunction = 'loadData1'
// the following to remove bad CORS warnings
delete $http.defaults.headers.common['X-Requested-With'];
$http.get(attrs.url).success(function(data) {
scope.content = data.ip;
})
},
};
})
app.directive('transformAndOutput', function() {
return {
restrict: 'E',
scope: true,
template: '<p>The value of content as called by {{callingFunction}} is: {{content}}</p>',
};
})