ngInclude automatically creates a child scope. You shouldn't need to explicitly pass some data to it since it can access its parent scope via prototypical inheritance (this might become a problem if your template changes the scope).
The problem here is that your template expects a firstName property to exist in the scope, but it doesn't. So you could change your template to
<div>
{{profile.firstName}}
</div>
but that would couple the template to the profile object, which might be a bad idea.
Another solution would be to manually create the firstName property in the correct scope:
<div ng-include src="'templates/profile.html'"
ng-init="firstName=profile.firstName">
</div>
I'm not very fond of this solution, though, because it can easily get out of hand if the template needs more properties and it breaks the template encapsulation to some extent.
And finally, you could wrap that template within a directive:
directive('whateverMakesSense', function() {
return {
restrict: 'E',
template: '<div>{{data.firstName}}</div>',
scope: { data: '=' }
};
});
...
<whatever-makes-sense data="profile"></whatever-makes-sense>
If you find yourself using that template in many places, I suggest you go for the custom directive approach. It will give you more control, things will be better encapsulated and as a bonus your markup will be more semantic - if you use anything but whatever-makes-sense, of course. :)
{{profile.firstName}}?