Creating a dynamic nested form in AngularJS by binding data to a model

Creating a nested form from a JSON object called formObject, I bind the values within the object itself. By recursively parsing the values, I extract the actual data, referred to as dataObject, upon submission.

To view the dataObject in a linear format, refer to this example: http://jsfiddle.net/DrQ77/80/.

<select ng-model="Answers[question.Name]" ng-options="option for option in question.Options">

In contrast, you can also see an example of recursion here: http://jsfiddle.net/DrQ77/92/. The variable question has been renamed to element to represent both questions and sections. Each section may contain multiple questions and subsections, demonstrating nesting levels.

Answers=[{
    section:"Personal",
    values:[{GenderQuestion:"Male"},{MaritalStatus:"Married"},{section:"Sub Personal",values:[{LivingWith:"Alone"}]}]
}, {
    section:"Random",
    values:[{ColorQuestion:"Red"}],
},
{SectionLess:"opt1"}]

This approach allows for nesting at various levels, which may not be achievable with $scope.Answers from the first example. However, when updating an existing dataObject, there is a need to map the dataObjects onto the formObject before rendering, then parse it again upon submission. While effective, this method lacks elegance due to its recursive nature, making me wonder if there is a more 'angular way' to accomplish this.

Has anyone successfully implemented a similar solution in a more efficient manner? How can I improve this process?

Answer №1

While it may not be the most aesthetically pleasing solution, here is an alternative to the proposed solution.

http://jsfiddle.net/DrQ77/84/

function QuestionController($scope) {
    $scope.answers = {};
    $scope.tempOption = "";
    $scope.questions = [
    {
        "text": "Gender?",
        "name": "GenderQuestion",
        "options": ["Male", "Female"]},
    {
        "text": "Favorite color?",
        "name": "ColorQuestion",
        "options": ["Red", "Blue", "Green"]}
    ];

    $scope.showAnswers = function () {
      console.log($scope.answers);
    };

    $scope.pumpOption = function (name, tempOption) {
      $scope.answers[name] = tempOption;
    };
};

<ul ng-repeat="question in questions">
    <li>
        <div>{{question.text}}</div>
        <select ng-model="tempOption" ng-options="opt for opt in question.options" ng-change="pumpOption(question.name, tempOption)">
        </select>
    </li>
</ul>    

In this implementation, we store the selected option in the select tag's value into a variable called $scope.tempOption.

We then trigger a function upon the ng-change event on the select tag, where we pass the values of $scope.tempOption and {{question.name}} associated with the select tag.

This function sets answers[name] to the current value of $scope.tempOption.

I hope this solution serves you well, best of luck :)

Answer №2

var data = {
    colors:["Red","Blue","Green","Black","White"],
    genders:["Male", "Female"],
    topics:["Gender", "Color"],
    categories:["Favorite", "Least favorite"],
};

function createQuestion(topic, category){
    return (category+1 ? data.categories[category] + ' ' : '') 
    + ' ' + data.topics[topic] + '?'
}

function QuestionController($scope){

    $scope.Answers = {};
    $scope.Questions = [
        {
            "Text": createQuestion(0),
            "Name": "GenderQuestion",
            "Options": data.genders 
        },{
            "Text": createQuestion(1,0),
            "Name": "ColorQuestion",
            "Options": data.colors.slice(0,3)
        },{
            "Text": createQuestion(0,1),
            "Name": "SexistQuestion",
            "Options": data.genders
        },{
            "Text": createQuestion(1,1),
            "Name": "RacistQuestion",
            "Options":data.colors.slice(3)
        }
    ];

    $scope.ShowAnswers = function()
    {
        console.log($scope.Answers);
    };
}    

Alright, no more joking around. Have you experimented with using a flat related object relational table approach rather than nesting?

{
  sections:[
    { "Name": 'Personal', "questions":[0], sub:[1] },
    { "Name": 'SubPersonal', "questions":[3], sub:[]},
    { "Name": 'Random',   "questions":[1,2], sub:[] }
  ],
  questions:[
    { "Name":"Gender",     "Text":"Gender?",         "Options":["Male", "Female"] },
    { "Name":"Color",      "Text":"Favorite Color?", "Options":["Red","Blue","Green"] },
    { "Name":"LivingWith", "Text":"Living With?",    "Options":["Alone","Someone"] },
    { "Name":"Random",     "Text":"SectionLess",     "Options":["opt1", "opt2"] }
  ]
}

Answer №3

all you need to do is initialize $scope.Answer = {}; and connect ng-model to it. Check out the example here: http://jsfiddle.net/2AwLM/40/

Answer №4

Hey there! This task turned out to be more challenging than expected, but I believe I've got it right. To achieve what you're looking for, you'll need to utilize a directive that dynamically generates its template HTML. It's even better to use templateUrl and a function that passes in the type.

For a quick overview, check out the fiddle at http://jsfiddle.net/cirrusinno/SzaNW/2

The structure of the HTML is quite straightforward

<div ng-controller="questionsController">

<!-- Render section or question for each item in Questions -->
<div ng-repeat="item in Questions">
    <!-- Ignore root sections here -->
    <question item="item"></question>
</div>

You'll have to develop a directive that can handle rendering both questions and sections. It should also keep track of the current section to assemble the answers

.directive('question', ['$compile', '$templateCache', function ($compile, $templateCache) {
    var generateHtmlTemplate = function (item, section) {
        return item.type != 'section' ?
        // Render the question    
        'question ' + item.name + '<br/>' +
            '<select ng-model="optionValue" ng-options="opt for opt in item.options" ng-change="pushAnswer(item.name, section, optionValue)"></select>' :
        // Or the template for a section
        '<br/><hr/>.section ' + item.sectionName + '<br/>' +
            '<div ng-repeat="q in item.questions"><question item="q" section="item"></question></div><hr/>';
    }

    var currentSection = null;

    return {
        scope: {
            item: '=',
            section: '='
        },
        restrict: 'E',
        link: function ($scope, element, attrs) {
            // Check current section
            if ($scope.item.type == 'section') {
                // Set new current section
                $scope.currentSection = $scope.item;
            } else {
                // Use section passed in from parent section
                $scope.currentSection = $scope.section;
            }

            // Get template HTML
            var t = generateHtmlTemplate($scope.item, $scope.currentSection);

            // Set the scope function to set the value
            $scope.pushAnswer = function (q, section, value) {
                // Build the Answers object here as needed...
                if (section != null) {
                    console.log('pushAnswer q=' + q.name + ' - section=' + section.sectionName + ' - value=' + value);
                } else {
                    console.log('pushAnswer q=' + q.name + ' - section=rootSection - value=' + value);
                }

            };

            // Inject the template into the element
            element.html(t);

            // Compile it and return it
            $compile(element.contents())($scope);
        },
    };
}])

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Increase the count for each category with the help of JavaScript

Currently, I am working on an application using PHP and have 180 vendors registered in the database. Each vendor has a unique ID stored in the database. I need to create a system where every time a user clicks on the "view details" button for a specific ...

Replace character within a table using jQuery

Below is the table content: <table> <tr> <td>"James"</td> <td>"do"</td> <td>"you</td> <td>"like</td> <td>"your life"</td> </tr> </table> <butt ...

"Converting an HTML table to a PDF or Excel file using AngularJS - A step-by-step

I've experimented with various approaches, but they all seem to have restrictions on the number of columns in the PDF or a height limitation. Additionally, when I open Excel files on my MAC, it displays as HTML which is not what I want. Can anyone rec ...

A thrilling twist on the classic game of Tic Tac Toe, where X and

I am having trouble with switching between the cross and circle symbols in my Tic Tac Toe game. The code seems to be set up correctly, but it's not functioning as expected. Any suggestions on how to fix this? Here is the JavaScript code: varcode va ...

Accessing an Array from a service method in Angular and passing it to my main component

Within my api-service.ts, I have an array that holds some data. public getData():Observable<any[]> { return Observable.toString[ObsResult]; } Now, in the main component, I am attempting to call the getData() method to render the data in ...

What is the process for obtaining the URL of the website that is hosting my iframe?

Do you have a technical inquiry? I am curious to know if it is feasible to retrieve the URL of the website that is hosting my iframe. The pages that host my iframe are utilizing the following code: <iframe id="vacancy-iframe" src="http://mypage.co ...

Exploring the functionality of inline easing when using the ScrollTo plug-in

Attempting to utilize the ScrollTo plug-in with an easing effect. I prefer not to include the easing plug-in file, as I will only use it once and for a single easing effect. The specific 'easeOutBack' function I aim to implement is: easeOutBac ...

Tips on verifying if user x has sent over two messages in the chat and concealing their identity

I have a JavaScript chat application that is causing me some trouble. I recently implemented a feature to display user names on top of messages, and while it works well for the first message, I am struggling to hide the user's name when they send subs ...

Increase the size of the SVG area

I'm still learning how to work with SVGs, so I appreciate your patience as I ask what may seem like a simple question. Currently, I have an SVG image that resembles a cake shape. See it here: Take a look at the code: <svg version="1" id="Layer_1 ...

Using AngularJS, generate a JSON array with a specified key

Looking to create a JSON array structure with keys using AngularJS, but unsure how to push data in order to achieve this. The goal is to generate a JSON array based on the provided data below. $scope.category = [{"id": 20, "name": "vegetable"}, {"id": ...

Halt the adhesion of the Bootstrap column when it hits the div section

I have a simple bootstrap row containing two columns: <div class="row"> <div class="col-xs-7"> <p>Walking together</p> </div> <div class="col-xs-5" id="stay"> <p>Joyful journey</p> </div ...

Programmatically toggle the visibility of an ion fab button

Looking for assistance in finding a method to toggle the visibility of a particular button within the collection of buttons in an ion-fab ...

javascriptchange the format from <string, string[]> to <string, string>

In my code, I came across a JavaScript object that consists of key/value pairs. Each value in this object is an array of strings: var errors = { "Message": ["Error #1", "Error #2"], "Email": ["Error #3", "Error #4"] }; My goal is to transform thi ...

Manually initializing Angular bootstrap with async in the Angular script tag

I am encountering an issue when trying to asynchronously download the Angular script in my application and manually bootstrap the application upon loading. The error message states: Failed to instantiate module wt due to: Error: [$injector:modulerr] htt ...

Display a complete calendar with the date picker on a webpage

Currently experimenting with React to build a straightforward application. Admiring the appearance of full calendars from these date pickers (once clicked): Material-UI React-Toolbox Wondering if it's feasible to present the full calendar directly ...

Sending an identifier to a PHP file using JavaScript

I am facing an issue with my JavaScript code where the id is supposed to be passed to my PHP script at intervals, but if the id stops being passed (indicating that the user has closed the JavaScript page), a specific block in the if statement should run in ...

Encountered a problem initializing Rangy.js on Internet Explorer

Currently, I am developing an angular application that utilizes textAngular along with rangy-core and rangy-selectionsaverestore. However, I am encountering some errors specifically on the latest version of Internet Explorer: Module 'WrappedSelection ...

Display the retrieved information from MongoDB onto a jade template

Upon checking the console, I see output similar to this: [ { __v: 0, city: 'on1', address: '111', first: 'user', last: 'one', chart: 'char1', doctor: 'doc1', _id: 5698a803d98f05482ba48a4b }, { __ ...

Error message: The context object is not iterable. Please note that it must be iterable in order to avoid

While going through a React course on Context API, I encountered an error that says "context is not iterable TypeError: context is not iterable". Has there been any new syntax introduced? If so, please let me know. Here is the content of app.js: I'v ...

The functionality of Jquery autocomplete _renderItem appears to be malfunctioning

There seems to be an issue with the _renderItem function as it is not executing at all. I even tried using console.log to debug but no messages are being printed. I also attempted using various attributes like 'autocomplete', 'ui-autocomplet ...