1

I'm trying to build a directive for my Angular to help with the integration of form fields. I've implemented Scott Allens solution from his Angular playbook, and it works fine for a normal stacked form.

I need however to adapt it to a horizontal form instead. Here's my code:

Markup

<div form-group>
    <label for="name">Name</label>
    <input type="text" id="name" ng-model="vm.name">
</div>

formGroup directive

function link(scope, element) {
    setupDom(element[0]);
}

function setupDom(element) {
    var label = element.querySelector("label");
    label.classList.add("control-label");

    var input = element.querySelector("input, textarea, select");
    var type = input.getAttribute("type");
    if (type !== "radio" && type !== "checkbox"){
        input.classList.add("form-control");
    }

    element.classList.add("form-group");
}

function formGroup() {
    return {
        restrict: "A",
        link: link
    }
}

The output becomes:

<div form-group="" class="form-group">
    <label for="name" class="control-label">Name</label>
    <input type="text" id="name" ng-model="vm.name" class="form-control">
</div>

And that's fine for stacked form. Since I need a horizontal form, my output needs to look like this:

<div form-group="" class="form-group">
    <label for="name" class="control-label col-sm-3">Name</label>

    <div class="col-sm-9">
        <input type="text" id="name" ng-model="vm.name" class="form-control">
    </div>
</div>

I've tried many solutions and I can get it work with single elements like an input, textarea or a select. It becomes much more tricky when I have something like two radio buttons inside my markup like this:

<div form-group>
    <label>Active</label>

    <div class="radio">
        <label>
            <input type="radio" name="active" ng-value="true" ng-model="vm.active"> Yes
        </label>
    </div>
    <div class="radio">
        <label>
            <input type="radio" name="active" ng-value="false" ng-model="vm.active"> No
        </label>
    </div>
</div>

The desired output of the above mentioned code should be:

<div form-group class="form-group">
    <label class="control-label col-sm-3">Active</label>

    <div class="col-sm-9">
        <div class="radio">
            <label>
                <input type="radio" name="active" ng-value="true" ng-model="vm.active"> Yes
            </label>
        </div>
        <div class="radio">
            <label>
                <input type="radio" name="active" ng-value="false" ng-model="vm.active"> No
            </label>
        </div>
    </div>
</div>

Please notice that the input(s) in the form-group is not fixed. It can be either a single input, textarea, select, a group of radio buttons or checkboxes. I'm lost for how I can make that happen. Any help is appreciated. Thanks!

UPDATE

I made some small changes to Mark Veenstra's code to make it (sort of) working:

function setupDom(element) {
    element.classList.add("form-group");

    var label = element.querySelector("label");
    label.classList.add("control-label", "col-sm-3");

    var input = element.querySelector("input, textarea, select");
    var type = input.getAttribute("type");
    if (type !== "radio" && type !== "checkbox"){
        input.classList.add("form-control");
        angular.element(input).wrap(angular.element('<div class="col-sm-9"></div>'));
    }

    var div_radio = element.querySelector("div[class='radio']");
    angular.element(div_radio).wrap(angular.element('<div class="col-sm-9"></div>'));
}

This does not work completely as intended with multiple radio inputs since it only wraps the <div> on the first radio input element.

The output from radio button example in my original post using Marks code is:

<div form-group="" class="form-group">
    <label class="control-label col-sm-3">Active</label>

    <div class="col-sm-9">
        <div class="radio">
            <label>
                <input type="radio" name="active" ng-value="true" ng-model="vm.active" value="true"> Yes
            </label>
        </div>
    </div>
    <div class="radio">
        <label>
            <input type="radio" name="active" ng-value="false" ng-model="vm.active" value="false"> No
        </label>
    </div>
</div>

SOLUTION

Check out the Plunker with the final result: http://plnkr.co/edit/Wv6V86hHTCz3URS9DhdU?p=preview

2
  • Can you share a plunker showing your attempt to achieve this? It will then be easier to see where you are going wrong. Commented Jun 5, 2015 at 13:10
  • @AbhishekJain check out this Plunker for the final result, if you're interested Commented Jun 5, 2015 at 15:56

2 Answers 2

1

In the angular.element documentation you can find the method wrap() to be able to wrap HTML around a selected element. Or see this direct link.

So what you could do in your directive is change the setupDom() function to match your requirements per type of form element.

function link(scope, element) {
    setupDom(element[0]);
}

function setupDom(element) {
    element.classList.add("form-group");

    var label = element.querySelector("label");
    label.classList.add("control-label col-sm-3");

    var input = element.querySelector("input, textarea, select");
    var type = input.getAttribute("type");
    if (type !== "radio" && type !== "checkbox"){
        input.classList.add("form-control");
        input.wrap(angular.element('<div class="col-sm-9"></div>'));
    }

    var div_radio = element.querySelectorAll("div[class='radio']");
    div_radio.wrap(angular.element('<div class="col-sm-9"></div>'));
}

function formGroup() {
    return {
        restrict: "A",
        link: link
    }
}

NOTE: This code is not tested, maybe there are some minor mistakes, but I guess you'll get the point now.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for the suggestion, @mark. Please take a look at my post below.
Maybe change element.querySelector("div[class='radio']") to element.querySelectorAll("div[class='radio']")
tried it, but it wrapped each radio group in it's own div. I updated my original question with the solution and link to a Plunker.
Looks like you got it working after some tips from me into the right direction. If you could give me some credit it would be nice.
1

Mark's suggestion came close, but it didn't solve my problem completely. I ended up using the following code in my formGroup directive:

(function (module) {
    "use strict";

    function link(scope, element) {
        setupDom(element[0]);
    }

    function setupDom(element) {
        element.classList.add("form-group");

        var children = angular.element(element).children();
        var labels = children.splice(0, 1);

        // Set label classes
        labels[0].classList.add("control-label", "col-sm-3");

        // Wrap children in div
        angular.element(children).wrapAll(angular.element("<div class='col-sm-9'></div>"));

        // Handle inputs
        var inputs = element.querySelectorAll("input, textarea, select");
        for (var i = 0, len = inputs.length; i < len; i++) {
            var input = inputs[i],
                type = input.getAttribute("type");

            if (type !== "radio" && type !== "checkbox") {
                input.classList.add("form-control");
            }
        }
    }

    function formGroup() {
        return {
            restrict: "A",
            link: link
        }
    }

    module.directive("formGroup", formGroup);

}(angular.module("app.core")));

Check out this Plunker to see it in action: http://plnkr.co/edit/Wv6V86hHTCz3URS9DhdU?p=preview

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.