I'm wanting to create a directive that renders an input based on the response from an api:
<input data-hyper-input="input.birthday" />
with the api response:
{
"input": {
"birthday": {
"type": "date",
"required": true,
"placeholder": "Enter your birthday"
}
}
}
should render:
<input type="date" name="birthday" data-ng-model="$values.birthday" placeholder="Enter your birthday" />
This works nice for simple input types. It starts falling down when you use type="select"
or type="textarea"
because html doesn't have a consistent way to write different input types:
<input type="text" />
<select>
<option value="item1">Item 1</option>
<option value="item2">Item 2</option>
<option value="item3">Item 3</option>
</select>
<textarea>this is my content</textarea>
instead of
<input type="text" />
<input type="select">
<option value="item1">Item 1</option>
<option value="item2">Item 2</option>
<option value="item3">Item 3</option>
</input>
<input type="textarea" value="this is my content" />
I thought this would be pretty easy to fix with angular... I started out with a simple solution:
<div data-ng-switch="input.type">
<select data-ng-switch-when="select" data-ng-model="input.$model" name="{{input.name}}" data-ng-required="input.required" data-hyper="input.options" data-ng-options="option.value as (option.name || option.text) for option in options"></select>
<textarea data-ng-switch-when="textarea" data-ng-model="input.$model" name="{{input.name}}" placeholder="{{input.placeholder}}" data-ng-required="input.required"></textarea>
<input data-ng-switch-default data-ng-model="input.$model" name="{{input.name}}" type="{{input.type}}" placeholder="{{input.placeholder}}" data-ng-required="input.required" />
</div>
This works except you have a wrapper div, which actually ends up being problematic for doing something like:
<input type="select" data-hyper-input="input.birthday" data-ng-change="inputChanged()" />
which actually ends up putting the ng-change
onto the div
instead of the input element so inputChanged()
never gets called.
I then tried the following:
<select data-ng-if="input.type == 'select'" data-ng-model="input.$model" name="{{input.name}}" data-ng-required="input.required" data-hyper="input.options" data-ng-options="option.value as (option.name || option.text) for option in options"></select>
<textarea data-ng-if="input.type == 'texarea'" data-ng-model="input.$model" name="{{input.name}}" placeholder="{{input.placeholder}}" data-ng-required="input.required"></textarea>
<input data-ng-if="input.type != 'select' && input.type != 'texarea'" data-ng-model="input.$model" name="{{input.name}}" type="{{input.type}}" placeholder="{{input.placeholder}}" data-ng-required="input.required" />
but angular only allows one root element in a directive.
I then tried using $elem.replaceWith
in the link function. This worked ok until you use it with a ng-repeat
<input data-ng-repeat="input in inputs" data-hyper-input="input">
with the api:
{
"inputs": [
{
"name": "name",
"type": "text",
"required": true,
"placeholder": "Enter your name"
},
{
"name": "birthday",
"type": "date",
"required": true,
"placeholder": "Enter your birthday"
},
{
"name": "favorite-color",
"type": "select",
"options": [
{"value": "red"},
{"value": "blue"},
{"value": "yellow"}
]
}
]
}
The way I wrote it unfortunately made the ng-repeat
re-add all of the input elements any time the list changed.
So if we add an item:
$scope.inputs.push({
type: 'text',
name: 'address',
placeholder: 'Enter your address (optional)'
});
we now end up with duplicated inputs:
<input type="text" name="name" data-ng-model="$values.name" placeholder="Enter your name" />
<input type="date" name="birthday" data-ng-model="$values.birthday" placeholder="Enter your birthday" />
<select name="favorite-color" data-ng-model="$values.favoriteColor">
<option value="red">red</option>
<option value="blue">blue</option>
<option value="yellow">yellow</option>
</select>
<input type="text" name="name" data-ng-model="$values.name" placeholder="Enter your name" />
<input type="date" name="birthday" data-ng-model="$values.birthday" placeholder="Enter your birthday" />
<select name="favorite-color" data-ng-model="$values.favoriteColor">
<option value="red">red</option>
<option value="blue">blue</option>
<option value="yellow">yellow</option>
</select>
<input type="text" name="address" data-ng-model="$values.address" placeholder="Enter your address (optional)" />