简体   繁体   中英

Dynamic form in AngularJS

I'm developing a CMS for a customer, it's all based on AngularJS with its controllers, views, services, etc.

What I need is a pattern where a dynamically loaded script injects some data in an existing scope .

Ok, in human words: I have a form managed by a controller . This form has several preset fields. These fields are managed by an array of the scope, something like:

$scope.fields = [
  { type: "text", name="first_name" },
  { type: "text", name="last_name" },
  { type: "email", name="email" }
];

The view prints dynamically the fields (ie it's a scaffolding).

When the customer log into the application I check if in his profile he has a custom script to load, if so the application appends a javascript to the DOM, the javascript file name is equal to the username of the logged user.

So, if the user is called " darko " and he has a custom script enabled, the application append this file to the DOM:

/js/customers/darko.js

Let's say that darko has further fields to show (and save) inside the form , how can I do that? I'd need to hook the controller so I can have access to its scope and then inject my fields. Something like:

var $scope = getUserFormScope();//some magic....
$scope.fields.push({ type: "text", name="skype" });

However, The form with further fields is just an example, what I really need, more generally, is a way to "hook controllers" and have access to theirs scopes.

Any idea?

SOLUTION

I've finally used the method suggested by marfarma. The custom script contains one or more partial controllers named with the same name of the controller they want to extend prefixed by Custom word, then I extend my controllers with these partial controllers. For example, my app has a controller named PageController, inside this controller I check if a CustomPageController exists:

if (typeof CustomPageController == 'function') {
    angular.extend(this, CustomPageController($scope));
}

if so, I extend the main controller with the custom one.

Here is a general way to "hook controllers" and have access to their scopes - mixin your hook code via angular.extend.

function CustomUserController($scope) {
    // contents of this hook controller is up to you

    var _this = this;

    // Mixin instance properties.
    this.vocalization = getValue('vocalization',$scope.user);
    this.runSpeed = getValue('runSpeed'        ,$scope.user);

    // Mixin instance methods.
    this.vocalize = function () {
        console.log(this.vocalization);
    };

    // Mixin scope properties.
    $scope.color = color;

    // Mixin scope methods.
    $scope.run = function(){
        console.log("run speed: " + _this.runSpeed );
    };
}

function PageController($scope) {

    var _this = this;
    $scope.user; // this should be the current user obj, with key for custom script lookup

    // Mixin Custom Script into Controller.
    if (userService.hasCustomScript($scope.user)) {
        angular.extend(this, new CustomUserController($scope));
    }
}

As for your specific example, one way to insert arbitrary fields into a form is to build it dynamically. I use a schema form directive that might work for your situation. Given a schema that defines the model properties, and an array that specified the items their order of inclusion, the directive lays out the form.

For example (see also this working plunker, incl. add'l features ):

<form class="form-inline" name="form" novalidate role="form">
    <div class="row-fluid clearfix">
        <h2>Sample Form</h2>
    </div>
    <div class="row-fluid clearfix">
        <hr> 
        <div class="span12">
            <fieldset class="span6">
                <schema-form-fields
                    fields="side1Fields"
                    model="model"
                    schema="modelSchema"
                    data="requestResult"
                    schema-list="schema">
                </schema-form-fields>
            </fieldset>
            <fieldset class="span6">
                <schema-form-fields
                    fields="side2Fields"
                    model="model"
                    schema="modelSchema"
                    data="requestResult"
                    schema-list="schema">
                </schema-form-fields>
            </fieldset>
        </div>
    </div>
    <div class="row-fluid clearfix">
        <button
            class="btn btn-primary span2 offset10"
            type="submit">
            Submit
        </button>
    </div>
</form>
// example controller with dynamic form
app.controller('HomeCtrl', ['$scope', 'schema', 'requestResult', 'dataService',
    function($scope, schema, requestResult, dataService) {

        $scope.modelSchema = schema.product;          
        $scope.model = {
            factoryDate: '20160506'
        };

        // field name arrays - one array per column in sample layout
        // insert elements into these and form should re-render
        // may require explicit watch to trigger update
        $scope.side1Fields = [
            'productName',
            'factoryDate'
        ];

        $scope.side2Fields = [
            'productType',
            'timeValue'
        ];  

        // ....  other controller code    
    }
]);
// example schema
app.value('schema', {
    "product": {
        "type": "object",
        "title": "Product Schema",
        "properties": {
            "productType": {
                "type": "string",
                "title": "Product Type",
                "showLabel": true,
                "tooltip": "Product classification",
                "readonly": false,
                "required": true,
                "class": "custom-select",
                "enum": ['Bike', 'Car', 'Airplane', 'Glider', 'Stilts']
            },
            "productName": {
                "title": "Product Name",
                "showLabel": true,
                "type": "string",
                "tooltip": "A more descriptive name for the modeled structure.",
                "readonly": false,
                "required": true
            },
            "factoryDate": {
                "title": "Factory Date",
                "type": "string",
                "showLabel": true,
                "control": "date",
                "dateFormat": "yyyymmdd",  // TODO format as provided 
                "tooltip": "Date of manufacture.",
                "dateOptions": {
                    autoclose: true
                },
                "readonly": false,
                "required": true
            },
            "timeValue": {
                "title": "Time (HHMM)",
                "showLabel": true,
                "type": "string",
                "pattern": "([0-1]{1}[0-9]{1}|20|21|22|23)[0-5]{1}[0-9]{1}",
                "timeFormat": "hhmm",  // TODO format as provided 
                "tooltip": "Time entry.",
                "readonly": false,
                "required": true,
            }
        }
    }
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM