JavaScript module to render and handle a form to add users

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
4
down vote

favorite
2












I recently switched to modular JavaScript and really like the idea of having the state of your application in JavaScript and not in the DOM. I want to know if what I am doing is considered best practices or not and how I can avoid re-rendering the entire list after each change.



First, the code:



HTML



<form id="add-user-form">
<input type="text" placeholder="username" name="username">
<button>add User</button>
</form>
<hr>
<ul id="user-list"></ul>


<script id="user-template" type="text/html">
 <li data-key="$key">$name</li>
</script>


JavaScript



// lets you get templates from HTML ( as you can see in the HTML above ). 
// This is to avoid writing out HTML inside JavaScript
const Template =
cached: ,
 get(templateId, placeholders)
let templateHtml = ''
if (!this.cached[templateId])
templateHtml = document.getElementById(templateId).innerHTML.trim()
this.cached[templateId] = templateHtml

else
templateHtml = this.cached[templateId]

 
for (key in placeholders)
templateHtml = templateHtml.replace(new RegExp("\$\s*" + key + "\s*", "g"), placeholders[key]);

return templateHtml
 


const Users =
users: ,
init()
this.cacheDom()
this.bindEvents()
,
cacheDom()
this.addUserFormEl = document.getElementById('add-user-form')
this.userListEl = document.getElementById('user-list')
,
bindEvents()
this.addUserFormEl.addEventListener('submit', this.handleAddUser.bind(this))
,
render()
// gets the HTML from ALL users of our user list and replaces the current user list
const userHtml = this.users.map( user => Template.get('user-template', user) ).join('')
this.userListEl.innerHTML = userHtml
,
handleAddUser(e)
e.preventDefault()

const username = e.currentTarget.elements['username'].value
if (!username) return
e.currentTarget.elements['username'].value = ''

const key = this.users.length // I know I know this is bad practice, please ignore :)

this.users.push(
key: key,
name: username
)

this.render()



Users.init()


The code has a form with an input field where you can type in a name that, once the form gets submitted, will be added to the user list below. It would probably be a good idea to seperate the userlist and the userform into seperate concerns, but for the sake of this example I kept it simple.



I want to keep DOM modifications to a minimum so I put them inside the method Users.render. This however has one side-effect. Whenever I add an user, it reloads the entire list. It might work with this amount of data, but lets say I have thousands of records, maybe even with input fields. These would all be emptied again.



I could of course just add the new entry to the DOM inside the Users.handleAddUser method but what if later I also want to delete users? I would have to delete the entry within Users.handleRemoveUser and would then have two methods that handle DOM modifications for the same thing.



I know this all can be easily and beautifully achieved with frameworks such as VueJs that use a vDOM but I am looking for a vanillaJs way of handling this.
(Please note that the code is just an example. Yes Unit testing and typeScript are missing and I am well aware of the fact that the code will not run in browsers that do not support ES6).







share|improve this question



























    up vote
    4
    down vote

    favorite
    2












    I recently switched to modular JavaScript and really like the idea of having the state of your application in JavaScript and not in the DOM. I want to know if what I am doing is considered best practices or not and how I can avoid re-rendering the entire list after each change.



    First, the code:



    HTML



    <form id="add-user-form">
    <input type="text" placeholder="username" name="username">
    <button>add User</button>
    </form>
    <hr>
    <ul id="user-list"></ul>


    <script id="user-template" type="text/html">
     <li data-key="$key">$name</li>
    </script>


    JavaScript



    // lets you get templates from HTML ( as you can see in the HTML above ). 
    // This is to avoid writing out HTML inside JavaScript
    const Template =
    cached: ,
     get(templateId, placeholders)
    let templateHtml = ''
    if (!this.cached[templateId])
    templateHtml = document.getElementById(templateId).innerHTML.trim()
    this.cached[templateId] = templateHtml

    else
    templateHtml = this.cached[templateId]

     
    for (key in placeholders)
    templateHtml = templateHtml.replace(new RegExp("\$\s*" + key + "\s*", "g"), placeholders[key]);

    return templateHtml
     


    const Users =
    users: ,
    init()
    this.cacheDom()
    this.bindEvents()
    ,
    cacheDom()
    this.addUserFormEl = document.getElementById('add-user-form')
    this.userListEl = document.getElementById('user-list')
    ,
    bindEvents()
    this.addUserFormEl.addEventListener('submit', this.handleAddUser.bind(this))
    ,
    render()
    // gets the HTML from ALL users of our user list and replaces the current user list
    const userHtml = this.users.map( user => Template.get('user-template', user) ).join('')
    this.userListEl.innerHTML = userHtml
    ,
    handleAddUser(e)
    e.preventDefault()

    const username = e.currentTarget.elements['username'].value
    if (!username) return
    e.currentTarget.elements['username'].value = ''

    const key = this.users.length // I know I know this is bad practice, please ignore :)

    this.users.push(
    key: key,
    name: username
    )

    this.render()



    Users.init()


    The code has a form with an input field where you can type in a name that, once the form gets submitted, will be added to the user list below. It would probably be a good idea to seperate the userlist and the userform into seperate concerns, but for the sake of this example I kept it simple.



    I want to keep DOM modifications to a minimum so I put them inside the method Users.render. This however has one side-effect. Whenever I add an user, it reloads the entire list. It might work with this amount of data, but lets say I have thousands of records, maybe even with input fields. These would all be emptied again.



    I could of course just add the new entry to the DOM inside the Users.handleAddUser method but what if later I also want to delete users? I would have to delete the entry within Users.handleRemoveUser and would then have two methods that handle DOM modifications for the same thing.



    I know this all can be easily and beautifully achieved with frameworks such as VueJs that use a vDOM but I am looking for a vanillaJs way of handling this.
    (Please note that the code is just an example. Yes Unit testing and typeScript are missing and I am well aware of the fact that the code will not run in browsers that do not support ES6).







    share|improve this question























      up vote
      4
      down vote

      favorite
      2









      up vote
      4
      down vote

      favorite
      2






      2





      I recently switched to modular JavaScript and really like the idea of having the state of your application in JavaScript and not in the DOM. I want to know if what I am doing is considered best practices or not and how I can avoid re-rendering the entire list after each change.



      First, the code:



      HTML



      <form id="add-user-form">
      <input type="text" placeholder="username" name="username">
      <button>add User</button>
      </form>
      <hr>
      <ul id="user-list"></ul>


      <script id="user-template" type="text/html">
       <li data-key="$key">$name</li>
      </script>


      JavaScript



      // lets you get templates from HTML ( as you can see in the HTML above ). 
      // This is to avoid writing out HTML inside JavaScript
      const Template =
      cached: ,
       get(templateId, placeholders)
      let templateHtml = ''
      if (!this.cached[templateId])
      templateHtml = document.getElementById(templateId).innerHTML.trim()
      this.cached[templateId] = templateHtml

      else
      templateHtml = this.cached[templateId]

       
      for (key in placeholders)
      templateHtml = templateHtml.replace(new RegExp("\$\s*" + key + "\s*", "g"), placeholders[key]);

      return templateHtml
       


      const Users =
      users: ,
      init()
      this.cacheDom()
      this.bindEvents()
      ,
      cacheDom()
      this.addUserFormEl = document.getElementById('add-user-form')
      this.userListEl = document.getElementById('user-list')
      ,
      bindEvents()
      this.addUserFormEl.addEventListener('submit', this.handleAddUser.bind(this))
      ,
      render()
      // gets the HTML from ALL users of our user list and replaces the current user list
      const userHtml = this.users.map( user => Template.get('user-template', user) ).join('')
      this.userListEl.innerHTML = userHtml
      ,
      handleAddUser(e)
      e.preventDefault()

      const username = e.currentTarget.elements['username'].value
      if (!username) return
      e.currentTarget.elements['username'].value = ''

      const key = this.users.length // I know I know this is bad practice, please ignore :)

      this.users.push(
      key: key,
      name: username
      )

      this.render()



      Users.init()


      The code has a form with an input field where you can type in a name that, once the form gets submitted, will be added to the user list below. It would probably be a good idea to seperate the userlist and the userform into seperate concerns, but for the sake of this example I kept it simple.



      I want to keep DOM modifications to a minimum so I put them inside the method Users.render. This however has one side-effect. Whenever I add an user, it reloads the entire list. It might work with this amount of data, but lets say I have thousands of records, maybe even with input fields. These would all be emptied again.



      I could of course just add the new entry to the DOM inside the Users.handleAddUser method but what if later I also want to delete users? I would have to delete the entry within Users.handleRemoveUser and would then have two methods that handle DOM modifications for the same thing.



      I know this all can be easily and beautifully achieved with frameworks such as VueJs that use a vDOM but I am looking for a vanillaJs way of handling this.
      (Please note that the code is just an example. Yes Unit testing and typeScript are missing and I am well aware of the fact that the code will not run in browsers that do not support ES6).







      share|improve this question













      I recently switched to modular JavaScript and really like the idea of having the state of your application in JavaScript and not in the DOM. I want to know if what I am doing is considered best practices or not and how I can avoid re-rendering the entire list after each change.



      First, the code:



      HTML



      <form id="add-user-form">
      <input type="text" placeholder="username" name="username">
      <button>add User</button>
      </form>
      <hr>
      <ul id="user-list"></ul>


      <script id="user-template" type="text/html">
       <li data-key="$key">$name</li>
      </script>


      JavaScript



      // lets you get templates from HTML ( as you can see in the HTML above ). 
      // This is to avoid writing out HTML inside JavaScript
      const Template =
      cached: ,
       get(templateId, placeholders)
      let templateHtml = ''
      if (!this.cached[templateId])
      templateHtml = document.getElementById(templateId).innerHTML.trim()
      this.cached[templateId] = templateHtml

      else
      templateHtml = this.cached[templateId]

       
      for (key in placeholders)
      templateHtml = templateHtml.replace(new RegExp("\$\s*" + key + "\s*", "g"), placeholders[key]);

      return templateHtml
       


      const Users =
      users: ,
      init()
      this.cacheDom()
      this.bindEvents()
      ,
      cacheDom()
      this.addUserFormEl = document.getElementById('add-user-form')
      this.userListEl = document.getElementById('user-list')
      ,
      bindEvents()
      this.addUserFormEl.addEventListener('submit', this.handleAddUser.bind(this))
      ,
      render()
      // gets the HTML from ALL users of our user list and replaces the current user list
      const userHtml = this.users.map( user => Template.get('user-template', user) ).join('')
      this.userListEl.innerHTML = userHtml
      ,
      handleAddUser(e)
      e.preventDefault()

      const username = e.currentTarget.elements['username'].value
      if (!username) return
      e.currentTarget.elements['username'].value = ''

      const key = this.users.length // I know I know this is bad practice, please ignore :)

      this.users.push(
      key: key,
      name: username
      )

      this.render()



      Users.init()


      The code has a form with an input field where you can type in a name that, once the form gets submitted, will be added to the user list below. It would probably be a good idea to seperate the userlist and the userform into seperate concerns, but for the sake of this example I kept it simple.



      I want to keep DOM modifications to a minimum so I put them inside the method Users.render. This however has one side-effect. Whenever I add an user, it reloads the entire list. It might work with this amount of data, but lets say I have thousands of records, maybe even with input fields. These would all be emptied again.



      I could of course just add the new entry to the DOM inside the Users.handleAddUser method but what if later I also want to delete users? I would have to delete the entry within Users.handleRemoveUser and would then have two methods that handle DOM modifications for the same thing.



      I know this all can be easily and beautifully achieved with frameworks such as VueJs that use a vDOM but I am looking for a vanillaJs way of handling this.
      (Please note that the code is just an example. Yes Unit testing and typeScript are missing and I am well aware of the fact that the code will not run in browsers that do not support ES6).









      share|improve this question












      share|improve this question




      share|improve this question








      edited Jun 26 at 23:15









      Sam Onela

      5,82961544




      5,82961544









      asked Mar 21 at 2:53









      Michael

      212




      212




















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          0
          down vote














          Whenever I add an user, it reloads the entire list.




          One alternate approach would be to have the handleAddUser() store the newly added user in a property (e.g. recentlyAdded) and then have the render() method look for that property - if it is set (to something other than null) then add the rendered template of the new user and clear that property.



          When the page loads, is there an existing list of users that gets added to the list? If so, maybe those could be stored in a different property and the existing code in render() could look for that property for rendering the existing records.




          I could of course just add the new entry to the DOM inside the Users.handleAddUser method but what if later, I also want to delete users. I would have to delete the entry within Users.handleRemoveUser and would so already have two methods that handle DOM modifications for the same thing.




          Going along with the alternate approach above, one could have the handleRemoveUser method also store the key of the recently removed user, and then the render() method can look for that property too and remove such an element associated with that key - perhaps by adding an id or other data attribute to the template.



          You didn't include an implementation for handleRemoveUser so I can only guess as to what it would be: some way of removing the user from the list this.user. If this.users is still an array, then looking for the user to remove might require a loop. However if this.users is an associative-array (i.e. object with keys corresponding to the key of each user) (or a Map), then looking for the user to remove can be achieved without a loop.



          See this demonstrated in the snippet below.






          // lets you get templates from HTML ( as you can see in the HTML above ). 
          // This is to avoid writing out HTML inside JavaScript
          const Template =
          cached: ,
          get(templateId, placeholders)
          let templateHtml = ''
          if (!this.cached[templateId])
          templateHtml = document.getElementById(templateId).innerHTML.trim()
          this.cached[templateId] = templateHtml

          else
          templateHtml = this.cached[templateId]


          for (key in placeholders)
          templateHtml = templateHtml.replace(new RegExp("\$\s*" + key + "\s*", "g"), placeholders[key]);

          return templateHtml



          const Users =
          users: ,
          recentlyAdded: null,
          recentlyRemovedKey: null,
          nextKey: 1,
          init()
          this.cacheDom()
          this.bindEvents()
          ,
          cacheDom()
          this.addUserFormEl = document.getElementById('add-user-form')
          this.userListEl = document.getElementById('user-list')
          ,
          bindEvents()
          this.addUserFormEl.addEventListener('submit', this.handleAddUser.bind(this))
          this.userListEl.addEventListener('click', this.handleListClick.bind(this));
          ,
          render()
          // gets the HTML from ALL users of our user list and replaces the current user list
          /*const userHtml = this.users.map( user => Template.get('user-template', user) ).join('')
          this.userListEl.innerHTML = userHtml*/
          if (this.recentlyAdded)
          this.userListEl.innerHTML += Template.get('user-template', this.recentlyAdded);
          this.recentlyAdded = null;

          if (this.recentlyRemovedKey)
          const element = document.getElementById(this.recentlyRemovedKey);
          if (element)
          element.remove();

          this.recentlyRemoved = null;

          console.log('userlist after render: ',this.users);
          ,
          handleAddUser(e)
          e.preventDefault()

          const username = e.currentTarget.elements['username'].value
          if (!username) return
          e.currentTarget.elements['username'].value = ''

          const key = this.nextKey++;

          this.users[key] =
          key: key,
          name: username
          ;
          this.recentlyAdded = this.users[key];

          this.render();
          ,
          handleListClick(e)
          const target = e.target;
          if (target.classList.contains('remove'))
          this.handleRemoveUser(target.parentNode.id);

          ,
          handleRemoveUser(key)
          delete this.users[key];
          this.recentlyRemovedKey = key;
          this.render();



          Users.init()

          .remove:before 
          content: 'X';

          <form id="add-user-form">
          <input type="text" placeholder="username" name="username">
          <button>add User</button>
          </form>
          <hr>
          <ul id="user-list"></ul>


          <script id="user-template" type="text/html">
          <li data-key="$key" id="$key">$name <button class="remove"></button></li>
          </script>






          In revision 2 you had this question, altered in revision 5:




          As you can see I am not getting the input by ID but by form.elements and then referencing the name.




          It is fine to do that, but because the form data doesn't get sent to the server-side, you could also use the id attribute on the input instead of the name attribute, and then fetch that element by id.






          share|improve this answer





















            Your Answer




            StackExchange.ifUsing("editor", function ()
            return StackExchange.using("mathjaxEditing", function ()
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            );
            );
            , "mathjax-editing");

            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "196"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            convertImagesToLinks: false,
            noModals: false,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );








             

            draft saved


            draft discarded


















            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f190088%2fjavascript-module-to-render-and-handle-a-form-to-add-users%23new-answer', 'question_page');

            );

            Post as a guest






























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            0
            down vote














            Whenever I add an user, it reloads the entire list.




            One alternate approach would be to have the handleAddUser() store the newly added user in a property (e.g. recentlyAdded) and then have the render() method look for that property - if it is set (to something other than null) then add the rendered template of the new user and clear that property.



            When the page loads, is there an existing list of users that gets added to the list? If so, maybe those could be stored in a different property and the existing code in render() could look for that property for rendering the existing records.




            I could of course just add the new entry to the DOM inside the Users.handleAddUser method but what if later, I also want to delete users. I would have to delete the entry within Users.handleRemoveUser and would so already have two methods that handle DOM modifications for the same thing.




            Going along with the alternate approach above, one could have the handleRemoveUser method also store the key of the recently removed user, and then the render() method can look for that property too and remove such an element associated with that key - perhaps by adding an id or other data attribute to the template.



            You didn't include an implementation for handleRemoveUser so I can only guess as to what it would be: some way of removing the user from the list this.user. If this.users is still an array, then looking for the user to remove might require a loop. However if this.users is an associative-array (i.e. object with keys corresponding to the key of each user) (or a Map), then looking for the user to remove can be achieved without a loop.



            See this demonstrated in the snippet below.






            // lets you get templates from HTML ( as you can see in the HTML above ). 
            // This is to avoid writing out HTML inside JavaScript
            const Template =
            cached: ,
            get(templateId, placeholders)
            let templateHtml = ''
            if (!this.cached[templateId])
            templateHtml = document.getElementById(templateId).innerHTML.trim()
            this.cached[templateId] = templateHtml

            else
            templateHtml = this.cached[templateId]


            for (key in placeholders)
            templateHtml = templateHtml.replace(new RegExp("\$\s*" + key + "\s*", "g"), placeholders[key]);

            return templateHtml



            const Users =
            users: ,
            recentlyAdded: null,
            recentlyRemovedKey: null,
            nextKey: 1,
            init()
            this.cacheDom()
            this.bindEvents()
            ,
            cacheDom()
            this.addUserFormEl = document.getElementById('add-user-form')
            this.userListEl = document.getElementById('user-list')
            ,
            bindEvents()
            this.addUserFormEl.addEventListener('submit', this.handleAddUser.bind(this))
            this.userListEl.addEventListener('click', this.handleListClick.bind(this));
            ,
            render()
            // gets the HTML from ALL users of our user list and replaces the current user list
            /*const userHtml = this.users.map( user => Template.get('user-template', user) ).join('')
            this.userListEl.innerHTML = userHtml*/
            if (this.recentlyAdded)
            this.userListEl.innerHTML += Template.get('user-template', this.recentlyAdded);
            this.recentlyAdded = null;

            if (this.recentlyRemovedKey)
            const element = document.getElementById(this.recentlyRemovedKey);
            if (element)
            element.remove();

            this.recentlyRemoved = null;

            console.log('userlist after render: ',this.users);
            ,
            handleAddUser(e)
            e.preventDefault()

            const username = e.currentTarget.elements['username'].value
            if (!username) return
            e.currentTarget.elements['username'].value = ''

            const key = this.nextKey++;

            this.users[key] =
            key: key,
            name: username
            ;
            this.recentlyAdded = this.users[key];

            this.render();
            ,
            handleListClick(e)
            const target = e.target;
            if (target.classList.contains('remove'))
            this.handleRemoveUser(target.parentNode.id);

            ,
            handleRemoveUser(key)
            delete this.users[key];
            this.recentlyRemovedKey = key;
            this.render();



            Users.init()

            .remove:before 
            content: 'X';

            <form id="add-user-form">
            <input type="text" placeholder="username" name="username">
            <button>add User</button>
            </form>
            <hr>
            <ul id="user-list"></ul>


            <script id="user-template" type="text/html">
            <li data-key="$key" id="$key">$name <button class="remove"></button></li>
            </script>






            In revision 2 you had this question, altered in revision 5:




            As you can see I am not getting the input by ID but by form.elements and then referencing the name.




            It is fine to do that, but because the form data doesn't get sent to the server-side, you could also use the id attribute on the input instead of the name attribute, and then fetch that element by id.






            share|improve this answer

























              up vote
              0
              down vote














              Whenever I add an user, it reloads the entire list.




              One alternate approach would be to have the handleAddUser() store the newly added user in a property (e.g. recentlyAdded) and then have the render() method look for that property - if it is set (to something other than null) then add the rendered template of the new user and clear that property.



              When the page loads, is there an existing list of users that gets added to the list? If so, maybe those could be stored in a different property and the existing code in render() could look for that property for rendering the existing records.




              I could of course just add the new entry to the DOM inside the Users.handleAddUser method but what if later, I also want to delete users. I would have to delete the entry within Users.handleRemoveUser and would so already have two methods that handle DOM modifications for the same thing.




              Going along with the alternate approach above, one could have the handleRemoveUser method also store the key of the recently removed user, and then the render() method can look for that property too and remove such an element associated with that key - perhaps by adding an id or other data attribute to the template.



              You didn't include an implementation for handleRemoveUser so I can only guess as to what it would be: some way of removing the user from the list this.user. If this.users is still an array, then looking for the user to remove might require a loop. However if this.users is an associative-array (i.e. object with keys corresponding to the key of each user) (or a Map), then looking for the user to remove can be achieved without a loop.



              See this demonstrated in the snippet below.






              // lets you get templates from HTML ( as you can see in the HTML above ). 
              // This is to avoid writing out HTML inside JavaScript
              const Template =
              cached: ,
              get(templateId, placeholders)
              let templateHtml = ''
              if (!this.cached[templateId])
              templateHtml = document.getElementById(templateId).innerHTML.trim()
              this.cached[templateId] = templateHtml

              else
              templateHtml = this.cached[templateId]


              for (key in placeholders)
              templateHtml = templateHtml.replace(new RegExp("\$\s*" + key + "\s*", "g"), placeholders[key]);

              return templateHtml



              const Users =
              users: ,
              recentlyAdded: null,
              recentlyRemovedKey: null,
              nextKey: 1,
              init()
              this.cacheDom()
              this.bindEvents()
              ,
              cacheDom()
              this.addUserFormEl = document.getElementById('add-user-form')
              this.userListEl = document.getElementById('user-list')
              ,
              bindEvents()
              this.addUserFormEl.addEventListener('submit', this.handleAddUser.bind(this))
              this.userListEl.addEventListener('click', this.handleListClick.bind(this));
              ,
              render()
              // gets the HTML from ALL users of our user list and replaces the current user list
              /*const userHtml = this.users.map( user => Template.get('user-template', user) ).join('')
              this.userListEl.innerHTML = userHtml*/
              if (this.recentlyAdded)
              this.userListEl.innerHTML += Template.get('user-template', this.recentlyAdded);
              this.recentlyAdded = null;

              if (this.recentlyRemovedKey)
              const element = document.getElementById(this.recentlyRemovedKey);
              if (element)
              element.remove();

              this.recentlyRemoved = null;

              console.log('userlist after render: ',this.users);
              ,
              handleAddUser(e)
              e.preventDefault()

              const username = e.currentTarget.elements['username'].value
              if (!username) return
              e.currentTarget.elements['username'].value = ''

              const key = this.nextKey++;

              this.users[key] =
              key: key,
              name: username
              ;
              this.recentlyAdded = this.users[key];

              this.render();
              ,
              handleListClick(e)
              const target = e.target;
              if (target.classList.contains('remove'))
              this.handleRemoveUser(target.parentNode.id);

              ,
              handleRemoveUser(key)
              delete this.users[key];
              this.recentlyRemovedKey = key;
              this.render();



              Users.init()

              .remove:before 
              content: 'X';

              <form id="add-user-form">
              <input type="text" placeholder="username" name="username">
              <button>add User</button>
              </form>
              <hr>
              <ul id="user-list"></ul>


              <script id="user-template" type="text/html">
              <li data-key="$key" id="$key">$name <button class="remove"></button></li>
              </script>






              In revision 2 you had this question, altered in revision 5:




              As you can see I am not getting the input by ID but by form.elements and then referencing the name.




              It is fine to do that, but because the form data doesn't get sent to the server-side, you could also use the id attribute on the input instead of the name attribute, and then fetch that element by id.






              share|improve this answer























                up vote
                0
                down vote










                up vote
                0
                down vote










                Whenever I add an user, it reloads the entire list.




                One alternate approach would be to have the handleAddUser() store the newly added user in a property (e.g. recentlyAdded) and then have the render() method look for that property - if it is set (to something other than null) then add the rendered template of the new user and clear that property.



                When the page loads, is there an existing list of users that gets added to the list? If so, maybe those could be stored in a different property and the existing code in render() could look for that property for rendering the existing records.




                I could of course just add the new entry to the DOM inside the Users.handleAddUser method but what if later, I also want to delete users. I would have to delete the entry within Users.handleRemoveUser and would so already have two methods that handle DOM modifications for the same thing.




                Going along with the alternate approach above, one could have the handleRemoveUser method also store the key of the recently removed user, and then the render() method can look for that property too and remove such an element associated with that key - perhaps by adding an id or other data attribute to the template.



                You didn't include an implementation for handleRemoveUser so I can only guess as to what it would be: some way of removing the user from the list this.user. If this.users is still an array, then looking for the user to remove might require a loop. However if this.users is an associative-array (i.e. object with keys corresponding to the key of each user) (or a Map), then looking for the user to remove can be achieved without a loop.



                See this demonstrated in the snippet below.






                // lets you get templates from HTML ( as you can see in the HTML above ). 
                // This is to avoid writing out HTML inside JavaScript
                const Template =
                cached: ,
                get(templateId, placeholders)
                let templateHtml = ''
                if (!this.cached[templateId])
                templateHtml = document.getElementById(templateId).innerHTML.trim()
                this.cached[templateId] = templateHtml

                else
                templateHtml = this.cached[templateId]


                for (key in placeholders)
                templateHtml = templateHtml.replace(new RegExp("\$\s*" + key + "\s*", "g"), placeholders[key]);

                return templateHtml



                const Users =
                users: ,
                recentlyAdded: null,
                recentlyRemovedKey: null,
                nextKey: 1,
                init()
                this.cacheDom()
                this.bindEvents()
                ,
                cacheDom()
                this.addUserFormEl = document.getElementById('add-user-form')
                this.userListEl = document.getElementById('user-list')
                ,
                bindEvents()
                this.addUserFormEl.addEventListener('submit', this.handleAddUser.bind(this))
                this.userListEl.addEventListener('click', this.handleListClick.bind(this));
                ,
                render()
                // gets the HTML from ALL users of our user list and replaces the current user list
                /*const userHtml = this.users.map( user => Template.get('user-template', user) ).join('')
                this.userListEl.innerHTML = userHtml*/
                if (this.recentlyAdded)
                this.userListEl.innerHTML += Template.get('user-template', this.recentlyAdded);
                this.recentlyAdded = null;

                if (this.recentlyRemovedKey)
                const element = document.getElementById(this.recentlyRemovedKey);
                if (element)
                element.remove();

                this.recentlyRemoved = null;

                console.log('userlist after render: ',this.users);
                ,
                handleAddUser(e)
                e.preventDefault()

                const username = e.currentTarget.elements['username'].value
                if (!username) return
                e.currentTarget.elements['username'].value = ''

                const key = this.nextKey++;

                this.users[key] =
                key: key,
                name: username
                ;
                this.recentlyAdded = this.users[key];

                this.render();
                ,
                handleListClick(e)
                const target = e.target;
                if (target.classList.contains('remove'))
                this.handleRemoveUser(target.parentNode.id);

                ,
                handleRemoveUser(key)
                delete this.users[key];
                this.recentlyRemovedKey = key;
                this.render();



                Users.init()

                .remove:before 
                content: 'X';

                <form id="add-user-form">
                <input type="text" placeholder="username" name="username">
                <button>add User</button>
                </form>
                <hr>
                <ul id="user-list"></ul>


                <script id="user-template" type="text/html">
                <li data-key="$key" id="$key">$name <button class="remove"></button></li>
                </script>






                In revision 2 you had this question, altered in revision 5:




                As you can see I am not getting the input by ID but by form.elements and then referencing the name.




                It is fine to do that, but because the form data doesn't get sent to the server-side, you could also use the id attribute on the input instead of the name attribute, and then fetch that element by id.






                share|improve this answer














                Whenever I add an user, it reloads the entire list.




                One alternate approach would be to have the handleAddUser() store the newly added user in a property (e.g. recentlyAdded) and then have the render() method look for that property - if it is set (to something other than null) then add the rendered template of the new user and clear that property.



                When the page loads, is there an existing list of users that gets added to the list? If so, maybe those could be stored in a different property and the existing code in render() could look for that property for rendering the existing records.




                I could of course just add the new entry to the DOM inside the Users.handleAddUser method but what if later, I also want to delete users. I would have to delete the entry within Users.handleRemoveUser and would so already have two methods that handle DOM modifications for the same thing.




                Going along with the alternate approach above, one could have the handleRemoveUser method also store the key of the recently removed user, and then the render() method can look for that property too and remove such an element associated with that key - perhaps by adding an id or other data attribute to the template.



                You didn't include an implementation for handleRemoveUser so I can only guess as to what it would be: some way of removing the user from the list this.user. If this.users is still an array, then looking for the user to remove might require a loop. However if this.users is an associative-array (i.e. object with keys corresponding to the key of each user) (or a Map), then looking for the user to remove can be achieved without a loop.



                See this demonstrated in the snippet below.






                // lets you get templates from HTML ( as you can see in the HTML above ). 
                // This is to avoid writing out HTML inside JavaScript
                const Template =
                cached: ,
                get(templateId, placeholders)
                let templateHtml = ''
                if (!this.cached[templateId])
                templateHtml = document.getElementById(templateId).innerHTML.trim()
                this.cached[templateId] = templateHtml

                else
                templateHtml = this.cached[templateId]


                for (key in placeholders)
                templateHtml = templateHtml.replace(new RegExp("\$\s*" + key + "\s*", "g"), placeholders[key]);

                return templateHtml



                const Users =
                users: ,
                recentlyAdded: null,
                recentlyRemovedKey: null,
                nextKey: 1,
                init()
                this.cacheDom()
                this.bindEvents()
                ,
                cacheDom()
                this.addUserFormEl = document.getElementById('add-user-form')
                this.userListEl = document.getElementById('user-list')
                ,
                bindEvents()
                this.addUserFormEl.addEventListener('submit', this.handleAddUser.bind(this))
                this.userListEl.addEventListener('click', this.handleListClick.bind(this));
                ,
                render()
                // gets the HTML from ALL users of our user list and replaces the current user list
                /*const userHtml = this.users.map( user => Template.get('user-template', user) ).join('')
                this.userListEl.innerHTML = userHtml*/
                if (this.recentlyAdded)
                this.userListEl.innerHTML += Template.get('user-template', this.recentlyAdded);
                this.recentlyAdded = null;

                if (this.recentlyRemovedKey)
                const element = document.getElementById(this.recentlyRemovedKey);
                if (element)
                element.remove();

                this.recentlyRemoved = null;

                console.log('userlist after render: ',this.users);
                ,
                handleAddUser(e)
                e.preventDefault()

                const username = e.currentTarget.elements['username'].value
                if (!username) return
                e.currentTarget.elements['username'].value = ''

                const key = this.nextKey++;

                this.users[key] =
                key: key,
                name: username
                ;
                this.recentlyAdded = this.users[key];

                this.render();
                ,
                handleListClick(e)
                const target = e.target;
                if (target.classList.contains('remove'))
                this.handleRemoveUser(target.parentNode.id);

                ,
                handleRemoveUser(key)
                delete this.users[key];
                this.recentlyRemovedKey = key;
                this.render();



                Users.init()

                .remove:before 
                content: 'X';

                <form id="add-user-form">
                <input type="text" placeholder="username" name="username">
                <button>add User</button>
                </form>
                <hr>
                <ul id="user-list"></ul>


                <script id="user-template" type="text/html">
                <li data-key="$key" id="$key">$name <button class="remove"></button></li>
                </script>






                In revision 2 you had this question, altered in revision 5:




                As you can see I am not getting the input by ID but by form.elements and then referencing the name.




                It is fine to do that, but because the form data doesn't get sent to the server-side, you could also use the id attribute on the input instead of the name attribute, and then fetch that element by id.






                // lets you get templates from HTML ( as you can see in the HTML above ). 
                // This is to avoid writing out HTML inside JavaScript
                const Template =
                cached: ,
                get(templateId, placeholders)
                let templateHtml = ''
                if (!this.cached[templateId])
                templateHtml = document.getElementById(templateId).innerHTML.trim()
                this.cached[templateId] = templateHtml

                else
                templateHtml = this.cached[templateId]


                for (key in placeholders)
                templateHtml = templateHtml.replace(new RegExp("\$\s*" + key + "\s*", "g"), placeholders[key]);

                return templateHtml



                const Users =
                users: ,
                recentlyAdded: null,
                recentlyRemovedKey: null,
                nextKey: 1,
                init()
                this.cacheDom()
                this.bindEvents()
                ,
                cacheDom()
                this.addUserFormEl = document.getElementById('add-user-form')
                this.userListEl = document.getElementById('user-list')
                ,
                bindEvents()
                this.addUserFormEl.addEventListener('submit', this.handleAddUser.bind(this))
                this.userListEl.addEventListener('click', this.handleListClick.bind(this));
                ,
                render()
                // gets the HTML from ALL users of our user list and replaces the current user list
                /*const userHtml = this.users.map( user => Template.get('user-template', user) ).join('')
                this.userListEl.innerHTML = userHtml*/
                if (this.recentlyAdded)
                this.userListEl.innerHTML += Template.get('user-template', this.recentlyAdded);
                this.recentlyAdded = null;

                if (this.recentlyRemovedKey)
                const element = document.getElementById(this.recentlyRemovedKey);
                if (element)
                element.remove();

                this.recentlyRemoved = null;

                console.log('userlist after render: ',this.users);
                ,
                handleAddUser(e)
                e.preventDefault()

                const username = e.currentTarget.elements['username'].value
                if (!username) return
                e.currentTarget.elements['username'].value = ''

                const key = this.nextKey++;

                this.users[key] =
                key: key,
                name: username
                ;
                this.recentlyAdded = this.users[key];

                this.render();
                ,
                handleListClick(e)
                const target = e.target;
                if (target.classList.contains('remove'))
                this.handleRemoveUser(target.parentNode.id);

                ,
                handleRemoveUser(key)
                delete this.users[key];
                this.recentlyRemovedKey = key;
                this.render();



                Users.init()

                .remove:before 
                content: 'X';

                <form id="add-user-form">
                <input type="text" placeholder="username" name="username">
                <button>add User</button>
                </form>
                <hr>
                <ul id="user-list"></ul>


                <script id="user-template" type="text/html">
                <li data-key="$key" id="$key">$name <button class="remove"></button></li>
                </script>





                // lets you get templates from HTML ( as you can see in the HTML above ). 
                // This is to avoid writing out HTML inside JavaScript
                const Template =
                cached: ,
                get(templateId, placeholders)
                let templateHtml = ''
                if (!this.cached[templateId])
                templateHtml = document.getElementById(templateId).innerHTML.trim()
                this.cached[templateId] = templateHtml

                else
                templateHtml = this.cached[templateId]


                for (key in placeholders)
                templateHtml = templateHtml.replace(new RegExp("\$\s*" + key + "\s*", "g"), placeholders[key]);

                return templateHtml



                const Users =
                users: ,
                recentlyAdded: null,
                recentlyRemovedKey: null,
                nextKey: 1,
                init()
                this.cacheDom()
                this.bindEvents()
                ,
                cacheDom()
                this.addUserFormEl = document.getElementById('add-user-form')
                this.userListEl = document.getElementById('user-list')
                ,
                bindEvents()
                this.addUserFormEl.addEventListener('submit', this.handleAddUser.bind(this))
                this.userListEl.addEventListener('click', this.handleListClick.bind(this));
                ,
                render()
                // gets the HTML from ALL users of our user list and replaces the current user list
                /*const userHtml = this.users.map( user => Template.get('user-template', user) ).join('')
                this.userListEl.innerHTML = userHtml*/
                if (this.recentlyAdded)
                this.userListEl.innerHTML += Template.get('user-template', this.recentlyAdded);
                this.recentlyAdded = null;

                if (this.recentlyRemovedKey)
                const element = document.getElementById(this.recentlyRemovedKey);
                if (element)
                element.remove();

                this.recentlyRemoved = null;

                console.log('userlist after render: ',this.users);
                ,
                handleAddUser(e)
                e.preventDefault()

                const username = e.currentTarget.elements['username'].value
                if (!username) return
                e.currentTarget.elements['username'].value = ''

                const key = this.nextKey++;

                this.users[key] =
                key: key,
                name: username
                ;
                this.recentlyAdded = this.users[key];

                this.render();
                ,
                handleListClick(e)
                const target = e.target;
                if (target.classList.contains('remove'))
                this.handleRemoveUser(target.parentNode.id);

                ,
                handleRemoveUser(key)
                delete this.users[key];
                this.recentlyRemovedKey = key;
                this.render();



                Users.init()

                .remove:before 
                content: 'X';

                <form id="add-user-form">
                <input type="text" placeholder="username" name="username">
                <button>add User</button>
                </form>
                <hr>
                <ul id="user-list"></ul>


                <script id="user-template" type="text/html">
                <li data-key="$key" id="$key">$name <button class="remove"></button></li>
                </script>






                share|improve this answer













                share|improve this answer



                share|improve this answer











                answered Apr 9 at 18:36









                Sam Onela

                5,82961544




                5,82961544






















                     

                    draft saved


                    draft discarded


























                     


                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function ()
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f190088%2fjavascript-module-to-render-and-handle-a-form-to-add-users%23new-answer', 'question_page');

                    );

                    Post as a guest













































































                    Popular posts from this blog

                    Greedy Best First Search implementation in Rust

                    Function to Return a JSON Like Objects Using VBA Collections and Arrays

                    C++11 CLH Lock Implementation