JavaScript Building a MEAN Application More TODO with the MEAN Stack Using Angular-wrapped JavaScript APIs

Susan Rusie
Susan Rusie
10,671 Points

Dropdown Menu Added that adds new todos to the database

I can't find any good documentation to figure out how to do a dropdown menu that continuously adds new items to the database. I'd like to be able to add new todos, save them, and have a dropdown appear when I edit each todo that shows all the todos I have typed in on each line I edited that I can either click on and chose or add a new one. Does anyone know how to make this happen? I would love to know how this is done to improve the app. Thanks in advance

3 Answers

James Churchill
STAFF
James Churchill
Treehouse Teacher

Susan,

I'd love to help, but I'm not sure if I completely understand how you want to incorporate a drop down list into the design of the application. When editing a todo, what would display in the drop down list?

Thanks ~James

Susan Rusie
Susan Rusie
10,671 Points

Basically, it would be adding new todos. Maybe it wouldn’t be as beneficial to add to this app. However, I am working on an web application with a two-column xeditable table that is a lesson plan for preschoolers and I would like this feature so that if it were used, new activities could be clicked in to be selected or added to the dropdown and saved, edited, or deleted to the database. The first column is the name of the subject and the second column is the corresponding activity to go with that subject. . Any help you can offer is greatly appreciated.

Susan Rusie
Susan Rusie
10,671 Points

This is my current index.html, but it hasn’t been committed to Github yet. That is the finishing touch I need to finish this app for now.

<html lang="en">
  <head>
<title></title>
  <meta charset="windows-1252">
  <meta charset="viewport" content="width=device-width, initial scale=1.0">
<link rel="stylesheet" href="/style/style.css" type="text/css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></link>
</head>
<body ng-app = "lessonPlanApp" ng-controller="MainCtrl">
<h1>LESSON PLAN</h1>
<table class="table table-hover table-condensed table-striped table-bordered">
  <tr ng-repeat="plan in plans" onchange="getSelectPlans.optionValue();">
   <td>
       <select id ="select">
       <option value>
        <span editable-text="plan.name" e-name="name" e-form="rowform" >
          {{ plan.name || 'empty' }}
        </span>
        <option>
    </select>


 <script>

 function  getSelectValue()
  {
  var selectedValue = document.getElementById("select").value;
  console.log(selectedValue);
  }

  function insertValue()
  {
  var select = document.getElementById("select"),
    txtVal = document.getElementById("val").value,
    newOption = document.createElement("OPTION"),
    newOptionVal = document.createTextNode(txtVal);

 newOption.appendChild(newOptionVal);
  select.insertBefore(newOption,select.firstChild);
  </script>
   <td>

<select id ="select">
       <option value>
        <span editable-text="plan.plan" e-name="name" e-form="rowform" >
          {{ plan.plan|| 'empty' }}
        </span>
        <option>
    </select>


 <script>

 function  getSelectValue()
  {
  var selectedValue = document.getElementById("select").value;
  console.log(selectedValue);
  }

  function insertValue()
  {
  var select = document.getElementById("select"),
    txtVal = document.getElementById("val").value,
    newOption = document.createElement("OPTION"),
    newOptionVal = document.createTextNode(txtVal);

 newOption.appendChild(newOptionVal);
  select.insertBefore(newOption,select.firstChild);
  </script

  </td>
   <td style="white-space: nowrap">
    <!-- form -->
    <form id="plan"
    <form editable-form name="rowform" onbeforesave="savePlan($data, user.id)" ng-show="rowform.$visible" class="form-buttons form-inline" shown="inserted == plan">
     <button type="submit" ng-disabled="rowform.$waiting" class="btn btn-primary">
      save
     </button>
     <button type="button" ng-disabled="rowform.$waiting" ng-click="rowform.$cancel()" class="btn btn-default">
      cancel
     </button>
    </form>
    <div class="buttons" ng-show="!rowform.$visible">
      <button type="button" class="btn btn-primary" ng-click="rowform.$show()">edit</button>
      <button type="button" class="btn btn-danger" ng-click="removePlan($index)">del</button>
    </div>
   </td>
  </tr>
 </table>
 <button type="button" class="btn btn-default" ng-click="addPlan()">Add row</button>
</div>
 <script src="/scripts/vendor.bundle.js"></script>
 <script src="/scripts/plan.bundle.js"></script>
</body>
</html>

The first label on the subject column seems to work, but none of the rest in that column or the activity columns. If you can help me make this work the way it should or if you have a better way to do this, I would greatly appreciate it. Thanks in advance.

James Churchill
James Churchill
Treehouse Teacher

Susan,

1) Would you be able to share the rest of the code for your application? At a minimum, I need to see the code for your "MainCtrl" controller.

2) Can you give me the background on why you added the getSelectValue and insertValue JavaScript functions? It's a little unusual to see script blocks in your HTML page when you're also using AngularJS.

3) I'm not familiar with some of the HTML attributes that you're using: editable-text, e-name, e-form, and editable-form. What is the purpose of those attributes?

Thanks ~James

Susan Rusie
Susan Rusie
10,671 Points

James,

Thanks again for your help. To answer your questions, 1) Here is a link to all of my most current code. https://github.com/slrusie/susan_cdlou_project/commit/db0f8e6de58daaa02a51ad6ec98ea8cb21ebd359

2) This is the video that I got that information for adding new options that has "getSelectValue" and "insertValue". Tjhis video was the only video that I could find that seemed to help the most. The problem is I would like for the dropdown and editing label to be hidden once a new plan is added to the dropdown and saved to MongoDB. I'm sure I can do it, but I need a little help pointing me in the right direction.
https://www.youtube.com/watch?time_continue=330&v=hIRjlG-gbuI

3) This is where I am getting "editable-text, e-name, e-form, and editable-form". This is what I want my app to look andd function like in the browser. http://vitalets.github.io/angular-xeditable/#editable-row. Thanks again for your help. It is greatly appreciated.

~Susan

Susan Rusie
Susan Rusie
10,671 Points

The other thing I meant mention is I would also like the “Add” button to be hidden as well as the label and dropdown when a new plan is saved to MongoDB and added to the dropdown menu. If you have ever seen the “AnyList” app on the Apple App Store, the “add”, “save”, and “delete” buttons are all hidden. There is a label at the top for adding items. When you click on the label, the keyboard pops up, you type in your new item, and press “done” to save it to the database and it is added to the list. There is a pencil next to every item on the list, which you can click on and a editing box pops up with a few things to change such as “quantity” or “notes” about the item that can be added and saved to the dayabase and your list again by pressing the “done” button. To delete an item from the list, you click on the item you want deleted and slide you finger to the left. The delete button appears, you press it, and the item is permanently deleted. That really would be the ideal way to finish this app, but I’ll be satisified just getting the add label, dropdown, and “add” button working and hidden when a new plan is saved. Thanks again for your help.

James Churchill
STAFF
James Churchill
Treehouse Teacher

Susan,

Sorry for the delay between my responses.

I've cloned your repo and got the app up and running locally. I'm still trying to understand the data relationship between subjects and activities.

Let's take a look at the subject "ART". In your seed data, you list a single activity: "Painting".

var Plans = [
    {"name": "MUSIC", "plan": "A Peanut Sitting On A Railroad Track"},
    {"name": "ART", "plan": "Painting"},
    {"name": "STORIES", "plan": "Where The Wild Things Are"},
    {"name": "FINE MOTOR ACTIVITIES", "plan": "Blocks"},
    {"name": "GROSS MOTOR ACTIVITIES", "plan": "Dancing"},  
];

Is the intention that the user could define one or more activities per subject? Or will a subject only have a single activity?

Thanks ~James

Susan Rusie
Susan Rusie
10,671 Points

James,
That’s ok. I understand. I know you are busy, but appreciate the help. It would be that they could define one or more activities per subject and new subjects and activities could be added and saved if you click the “Add” button at the bottom of the table. As I edit each subject and activity, I want the new options to appear in each of the dropdown menus to click on to select and save and be able to delete the options add to each dropdown menu individually. I also want the dropdown menus to be hidden once the new options are selected and saved or deleted. Thanks in advance for your help. It is greatly appreciated.

Susan Rusie
Susan Rusie
10,671 Points

These were the changes I needed to make. I got help from a Code Louisville mentor and was able to finish my project that I was working on. Sorry it took so long to post.

controllers/main.js

use strict';

function MainCtrl($scope, dataService) {
  dataService.getPlans(function(response) {
    var plans = response.data.plans;
    $scope.plans = plans;
  });

  // remove plan
 $scope.removePlan = function(index) {
  var plan = $scope.plans[index];
  dataService.deletePlan(plan).then(() => {
    $scope.plans.splice(index, 1);
    console.log('successfully deleted plan');
  });
};

  // add new plan
  $scope.addPlan = function() {
    // line 43 of index.html drives whether or not the form is shown when you add a new plan, but what
    // it's referencing (inserted) didn't exist. I added it here and now the edit form shows when you
    // add a new plan
    $scope.inserted = {
      name: '',
      plans: [],
      selectedPlanIndex: 0,
      completed: false
    };

    $scope.plans.unshift($scope.inserted);
  };

  $scope.addPlanToSubject = function(planName, subject) {
    if (!subject.plans) subject.plans = [];
    subject.plans.push(planName);
    subject.selectedPlanIndex = subject.plans.indexOf(planName);
    document.querySelectorAll('#new-sub-plan').forEach(el => (el.value = null));
  };

  // this didn't exist yet. It will call savePlans whenever you click a save button on one of the rows
  $scope.savePlan = function(data, id) {
    dataService.savePlans($scope.plans);
  };
}
module.exports = MainCtrl;

controllers/plan.js

use strict';

function PlanCtrl($scope, dataService) {



  $scope.deletePlan = function (plan, index) {

    dataService.deletePlan(plan).then(function () {

      $scope.plans.splice(index, 1);

    });

  };



  $scope.savePlans = function () {

    var filteredPlans = $scope.plans.filter(function (plan) {

      if (plan.edited) {

        return plan

      };

    })

    dataService.savePlans(filteredPlans)

      .finally($scope.resetPlanState());

  };



  $scope.resetPlanState = function () {

    $scope.plans.forEach(function (plan) {

      plan.edited = false;

    });

  }

}



module.exports = PlanCtrl; 

scripts/services/data.

'use strict';

function DataService ($http, $q) {

  this.getPlans = function(cb) {
    $http.get('/api/plans').then(cb);
  };
this.deletePlan = function(plan) {
    if (!plan._id) {
        return $q.resolve();
    }
    return $http.delete('/api/plans/' + plan._id).then(function () {
        console.log("I deleted the " + plan.name + " plan"); 
    });
};

  this.savePlans = function(plans) {
    var queue = [];
    plans.forEach(function(plan) {
      var request;
      if(!plan._id) {
        request = $http.post('/api/plans', plan)
      } else {
        request = $http.put('/api/plans/' + plan._id, plan).then(function(result) {
          plan = result.data.plan;
          return plan;
        })
      };
      queue.push(request);
    });
    return $q.all(queue).then(function(results) {
      console.log("I saved " + plans.length + " plans!");
    });
  };
}

module.exports = DataService;```

app/app.js

'use strict';



var angular = require('angular');



var app = angular.module('lessonPlanApp', ["xeditable"]);

app.run(['editableOptions', function(editableOptions) {

    editableOptions.theme = 'bs3';

}]);



require('./scripts/services');

require('./scripts/directives');

require('./scripts/controllers');

public/index.html

<html lang="en">



<head>

  <title></title>

  <meta charset="windows-1252">

  <meta charset="viewport" content="width=device-width, initial scale=1.0">

  <link rel="stylesheet" href="/style/style.css" type="text/css">

  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"

    crossorigin="anonymous">



</head>



<body>

  <div ng-app="lessonPlanApp" ng-controller="MainCtrl">

    <h1>LESSON PLAN</h1>

    <table class="table table-hover table-condensed table-striped table-bordered">

      <thead>

        <tr>

          <th>Subject</th>

          <th>Plan</th>

          <th>Actions</th>

        </tr>

      </thead>

      <tbody>

        <tr ng-repeat="plan in plans">

          <td>

            <span editable-text="plan.name" e-name="name" e-form="rowform">

              {{ plan.name || 'empty' }}

            </span>

          </td>

          <td>

            <!--  editable-select gives you a dropdown to select a subplan. The input beneath it will allow you to
                  insert a new plan into the plan's subplans -->

            <span editable-select="plan.selectedPlanIndex" e-name="selectedPlan" e-form="rowform" e-ng-options="index as p for (index, p) in plan.plans">

              <!--  the selectedPlanIndex indicates which subplan is currently selected -->

              {{ plan.plans[plan.selectedPlanIndex] }}

            </span>

            <input type="text" ng-show="rowform.$visible" ng-model="newSubPlan" id="new-sub-plan">

            <button ng-show="rowform.$visible" ng-click="addPlanToSubject(newSubPlan, plan)">Add</button>

          </td>

          <td style="white-space: nowrap">

            <form id="plan" editable-form name="rowform" onbeforesave="savePlan($data, plan._id)" ng-show="rowform.$visible" class="form-buttons form-inline"

              shown="inserted == plan">

              <button type="submit" ng-disabled="rowform.$waiting" class="btn btn-primary">

                save

              </button>

              <button type="button" ng-disabled="rowform.$waiting" ng-click="rowform.$cancel()" class="btn btn-default">

                cancel

              </button>

            </form>

            <div class="buttons" ng-show="!rowform.$visible">

              <button type="button" class="btn btn-primary" ng-click="rowform.$show()">edit</button>

              <button type="button" class="btn btn-danger" ng-click="removePlan($index)">del</button>

            </div>

          </td>

        </tr>

      </tbody>

    </table>

    <button type="button " class="btn btn-default" ng-click="addPlan() ">Add row</button>

  </div>

  <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>

  <script src="/scripts/vendor.bundle.js"></script>

  <script src="/scripts/plan.bundle.js"></script>

  <script>
    function insertValue() {
      var select = document.getElementById("select"),
        txtVal = document.getElementById("val").value,
        newOption = document.createElement("OPTION"),
        newOptionVal = document.createTextNode(txtVal);
      newOption.appendChild(newOptionVal);
      select.insertBefore(newOption, select.firstChild);
    }
  </script>

  <script>
    $(document).ready(function () {
      $('<option value="option"></option>').appendTo("#ddList");
    });
  </script>



  <script>
    $(document).ready(function () {
      $("#select").click(function () {
        $("id").hide();
      });
      $("#select").click(function () {
        $("id").show();
      });
    });
  </script>

</body>



</html>

public/mock/plans.json

[ 
    {"MUSIC"; "A Peanut Sitting On A Railroad Track"}    
]

[
    {"ART"; "Painting"}
]

[  
    {"STORIES"; "Where The Wild Things Are"}
]

[
    {"FINE MOTOR ACTIVITIES"; "Blocks"}
]

[
    {"GROSS MOTOR ACTIVITIES"; "Dancing"}
]