Personalização do modelo dentro de uma diretiva

Eu tenho um formulário que está usando marcação de Bootstrap, como o seguinte:

 
Legend text

Há um monte de código clichê lá, que eu gostaria de reduzir para uma nova diretiva – forma de input, como segue:

  

gera:

  

Eu tenho muito trabalho por meio de um modelo simples.

 angular.module('formComponents', []) .directive('formInput', function() { return { restrict: 'E', scope: { label: 'bind', formId: 'bind' }, template: '
' + '' + '
' + '' + '
' + '
' } })

No entanto, é quando eu venho para adicionar funcionalidades mais avançadas que estou ficando preso.

Como posso suportar valores padrão no modelo?

Gostaria de expor o parâmetro “type” como um atributo opcional na minha diretiva, por exemplo:

   

No entanto, se nada for especificado, eu gostaria de usar o padrão "text" . Como posso suportar isso?

Como posso personalizar o modelo com base na presença / ausência de atributos?

Também gostaria de poder suportar o atributo “obrigatório”, se estiver presente. Por exemplo:

  

Se required está presente na diretiva, gostaria de adicioná-lo ao gerado na saída e ignorá-lo de outra forma. Não tenho certeza de como conseguir isso.

Eu suspeito que esses requisitos podem ter ultrapassado um modelo simples e ter que começar a usar as fases de pré-compilation, mas não sei por onde começar.

 angular.module('formComponents', []) .directive('formInput', function() { return { restrict: 'E', compile: function(element, attrs) { var type = attrs.type || 'text'; var required = attrs.hasOwnProperty('required') ? "required='required'" : ""; var htmlText = '
' + '' + '
' + '' + '
' + '
'; element.replaceWith(htmlText); } }; })

Tentei usar a solução proposta por Misko, mas na minha situação, alguns atributos, que precisavam ser mesclados no meu template html, eram eles mesmos diretivas.

Infelizmente, nem todas as diretivas referenciadas pelo modelo resultante funcionaram corretamente. Eu não tive tempo suficiente para mergulhar no código angular e descobrir a causa raiz, mas encontrei uma solução alternativa, que poderia ser útil.

A solução foi mover o código, que cria o template html, da compilation para uma function de template. Exemplo baseado no código acima:

  angular.module('formComponents', []) .directive('formInput', function() { return { restrict: 'E', template: function(element, attrs) { var type = attrs.type || 'text'; var required = attrs.hasOwnProperty('required') ? "required='required'" : ""; var htmlText = '
' + '' + '
' + '' + '
' + '
'; return htmlText; } compile: function(element, attrs) { //do whatever else is necessary } } })

As respostas acima, infelizmente, não funcionam bem. Em particular, o estágio de compilation não tem access ao escopo, portanto, você não pode personalizar o campo com base em atributos dynamics. Usar o estágio de vinculação parece oferecer a maior flexibilidade (em termos de criação assíncrona de dom etc.). A abordagem abaixo aborda que:

  
 // directive angular.module('app') .directive('formField', function($compile, $parse) { return { restrict: 'E', compile: function(element, attrs) { var fieldGetter = $parse(attrs.field); return function (scope, element, attrs) { var template, field, id; field = fieldGetter(scope); template = '..your dom structure here...' element.replaceWith($compile(template)(scope)); } } } }) 

Eu criei uma essência com código mais completo e um writeup da abordagem.

Aqui está o que acabei usando.

Eu sou muito novo no AngularJS, então adoraria ver soluções melhores / alternativas.

 angular.module('formComponents', []) .directive('formInput', function() { return { restrict: 'E', scope: {}, link: function(scope, element, attrs) { var type = attrs.type || 'text'; var required = attrs.hasOwnProperty('required') ? "required='required'" : ""; var htmlText = '
' + '' + '
' + '' + '
' + '
'; element.html(htmlText); } } })

Exemplo de uso: