How to create more organized codes?

1

I already have some time to develop with jquery and I can already create anything that I intend to do with the language.

But something has been bothering me for some time, it's the organization of the code in JQUERY

I always start the code in a totally procedural way. When done I try to refactor the code by dividing everything into functions, but it is still a disruptive mess.

For example, this week I was editing a code for the form with steps see:

HTML

<div class="wrap">
  <div class="steps">
     <form id="step1"></form>
     <button class="next"></button>
  </div>
  <div class="steps">
     <form id="step2"></form>
     <button class="prev"></button>
     <button class="next"></button>
  </div>
  <div class="steps">
     <form id="step3"></form>
     <button class="prev"></button>
     <button class="save"></button>
  </div>
</div>

CSS

.wrap{
     width: 300%;
     position:relative;
 }
 .steps{
     width: calc(100% / 3);
     float: left;
 }

JS

$(document).ready(function(){
    init();
    $('.next').click(function(){
        var n_clicked = $(this);
        next(n_clicked);
        return false;
    });
    $('.prev').click(function(){
        prev();
        return false;
    });
    $('.save').click(function(){
        save()
        return false;
    });

});
function init(){
    wr = $('.wrap').width()/3; //wrap
    rt = 0; //right
    st = 1; //step
}
function next(next){       
    if(st < 3){
        rt = rt + wr;
        st++;
        $(".wrap").animate({'right':rt},2000);
        var form = next.parent().attr('id');
        if(form=='step1'){
            $.post({
                url: '/teste_mvc/turma/step1/' ,
                data: $('#step1').serialize(),
                success: function (e){
                    //alert(e);
                }
            });
        }
        else if(form=='step2'){
            $.post({
                url: '/teste_mvc/turma/step2/' ,
                data: $('#step2').serialize(),
                success: function (e){
                    //alert(e);
                }
            });
        }
    }
}
function prev(prev){ 
    if(st>1){
        rt = rt - wr;
        st--;
        $(".wrap").animate({'right':rt},2000);
    }
}
function save(save){ 
    $.post({
        url: '/teste_mvc/turma/step3/' ,
        data: $('#step3').serialize(),
        success: function (e){
            //alert(e);
        }
    });
    $('.steps').fadeOut();
    $('.wrap').css('right','0px');
    $('.wrap').css('width','100%');
    $('.relatorio_turma').fadeIn();        
    $('.relatorio_turma').load("/teste_mvc/Turma/Relatorio");
    rt = 0;
    st = 1;
}

I would like to know if there are more advanced features for code organization than simply modularizing the application.

    
asked by anonymous 21.05.2016 / 15:39

1 answer

0

Adriano, in your example above, you are trying to develop a Wizard.

Regardless of the number of steps or business rules, we have a basic structure common to this type of component.

Then for the component, you will need the following structure:

HTML Template

<template id="tmplStep">
  <div class="step hide">
    <div class="title">

    </div>
    <div class="content">

    </div>
    <div class="actions">
      <input class="prev" type="button" value="Anterior" />
      <input class="next" type="button" value="Proximo" />
    </div>
  </div>
</template>

CSS

.hide {
  display: none;
}

JavaScript

var Wizard = function (element) {
  var that = this;
  this.dom = {};
  this.dom.wrapper = element;
  this.dom.steps = this.dom.wrapper.querySelectorAll("[data-step]");  

  this.steps = [];
  [].forEach.call(this.dom.steps, function (element, indice) {
    var step = new Step(that, element);
  });
  this.steps[0].Show();
}

var Step = function (wizard, element) {
  var that = this;  
  this.wizard = wizard;
  this.indice = this.wizard.steps.length;
  this.wizard.steps.push(this);

  this.dom = {};
  this.dom.fragment = document.importNode(this.template, true);
  this.dom.wrapper = this.dom.fragment.querySelector(".step");
  this.dom.title = this.dom.wrapper.querySelector(".title");
  this.dom.content = this.dom.wrapper.querySelector(".content");
  this.dom.actions = this.dom.wrapper.querySelector(".actions");
  this.dom.prev = this.dom.actions.querySelector(".prev");
  this.dom.next = this.dom.actions.querySelector(".next");

  this.dom.content.appendChild(element);
  this.wizard.dom.wrapper.appendChild(this.dom.wrapper);

  this.dom.prev.addEventListener("click", function (event) {
    that.onPrevClick();
  });

  this.dom.next.addEventListener("click", function (event) {
    that.onNextClick();
  })
}

Step.prototype.template = document.getElementById("tmplStep").content;

Step.prototype.Show = function () {
  this.dom.wrapper.classList.remove("hide");  
  this.dom.prev.disabled = this.isPrimeiro;
  this.dom.next.value = this.isUltimo ? "Finalizar" : "Proximo";
}

Step.prototype.Hide = function () {
  this.dom.wrapper.classList.add("hide");
}

Step.prototype.onPrevClick = function () {
  this.Hide();
  this.prev.Show();
};

Step.prototype.onNextClick = function () {  
  var isValid = true;
  if (this.onValida) 
    isValid = this.onValida();
  if (isValid) {
    if (this.isUltimo) {
      if (this.wizard.onFinaliza) 
        this.wizard.onFinaliza();
    } else {
      this.Hide();
      this.next.Show();
    }
  }  
};

Object.defineProperty(Step.prototype, "prev", {
  get: function() { return this.wizard.steps[this.indice - 1]; }
});

Object.defineProperty(Step.prototype, "next", {
  get: function() { return this.wizard.steps[this.indice + 1]; }
});

Object.defineProperty(Step.prototype, "isPrimeiro", {
  get: function() { return this.indice == 0; }
});

Object.defineProperty(Step.prototype, "isUltimo", {
  get: function() { return this.indice == this.wizard.steps.length - 1; }
});

You can use the script above whenever you need to mount a "Wizard", however it is interesting that you load it into a separate file, if you prefer you can even mount a module for curljs or requirejs . >

Once this is done, just use it as follows:

** Markup HTML **

<div data-wizard="">
  <div data-step="Titulo 1">
    <label>
      Input 1:
      <input id="input1" type="text" required/>
    </label>
  </div>
  <div data-step="Titulo 2">
    <label>
      Input 2:
      <input id="input2" type="number" required/>
    </label>
  </div>
  <div data-step="Titulo 3">
    <label>
      Input 3:
      <input id="input3" type="date" required/>
    </label>
  </div>
</div>

JavaScript

var element = document.querySelector("[data-wizard]");
var wizard = new Wizard(element);
wizard.steps[0].onValida = function () {
  var input1 = document.getElementById("input1");
  var texto = input1.value.trim();
  if (!texto) alert("infore um texto não vazio!");
  return texto;
}
wizard.steps[1].onValida = function () {
  var input2 = document.getElementById("input2");
  var numero = input2.valueAsNumber;
  if (!numero) alert("infore um numero e que seja diferente de zero!");
  return numero;
}
wizard.steps[2].onValida = function () {
  var input3 = document.getElementById("input3");
  var data = input3.valueAsDate;
  if (!data) alert("infore uma data!");
  return data;
}
wizard.onFinaliza = function () {
  alert("Wizard Finalizado!");
}

Now the complete example:

var Wizard = function (element) {
  var that = this;
  this.dom = {};
  this.dom.wrapper = element;
  this.dom.steps = this.dom.wrapper.querySelectorAll("[data-step]");  
  
  this.steps = [];
  [].forEach.call(this.dom.steps, function (element, indice) {
    var step = new Step(that, element);
  });
  this.steps[0].Show();
}

var Step = function (wizard, element) {
  var that = this;  
  this.wizard = wizard;
  this.indice = this.wizard.steps.length;
  this.wizard.steps.push(this);

  this.dom = {};
  this.dom.fragment = document.importNode(this.template, true);
  this.dom.wrapper = this.dom.fragment.querySelector(".step");
  this.dom.title = this.dom.wrapper.querySelector(".title");
  this.dom.content = this.dom.wrapper.querySelector(".content");
  this.dom.actions = this.dom.wrapper.querySelector(".actions");
  this.dom.prev = this.dom.actions.querySelector(".prev");
  this.dom.next = this.dom.actions.querySelector(".next");
  
  this.dom.content.appendChild(element);
  this.wizard.dom.wrapper.appendChild(this.dom.wrapper);
  
  this.dom.prev.addEventListener("click", function (event) {
    that.onPrevClick();
  });

  this.dom.next.addEventListener("click", function (event) {
    that.onNextClick();
  })
}

Step.prototype.template = document.getElementById("tmplStep").content;

Step.prototype.Show = function () {
  this.dom.wrapper.classList.remove("hide");  
  this.dom.prev.disabled = this.isPrimeiro;
  this.dom.next.value = this.isUltimo ? "Finalizar" : "Proximo";
}

Step.prototype.Hide = function () {
  this.dom.wrapper.classList.add("hide");
}

Step.prototype.onPrevClick = function () {
  this.Hide();
  this.prev.Show();
};

Step.prototype.onNextClick = function () {  
  var isValid = true;
  if (this.onValida) 
    isValid = this.onValida();
  if (isValid) {
    if (this.isUltimo) {
      if (this.wizard.onFinaliza) 
        this.wizard.onFinaliza();
    } else {
      this.Hide();
      this.next.Show();
    }
  }  
};

Object.defineProperty(Step.prototype, "prev", {
  get: function() { return this.wizard.steps[this.indice - 1]; }
});

Object.defineProperty(Step.prototype, "next", {
  get: function() { return this.wizard.steps[this.indice + 1]; }
});

Object.defineProperty(Step.prototype, "isPrimeiro", {
  get: function() { return this.indice == 0; }
});

Object.defineProperty(Step.prototype, "isUltimo", {
  get: function() { return this.indice == this.wizard.steps.length - 1; }
});

var element = document.querySelector("[data-wizard]");
var wizard = new Wizard(element);
wizard.steps[0].onValida = function () {
  var input1 = document.getElementById("input1");
  var texto = input1.value.trim();
  if (!texto) alert("infore um texto não vazio!");
  return texto;
}
wizard.steps[1].onValida = function () {
  var input2 = document.getElementById("input2");
  var numero = input2.valueAsNumber;
  if (!numero) alert("infore um numero e que seja diferente de zero!");
  return numero;
}
wizard.steps[2].onValida = function () {
  var input3 = document.getElementById("input3");
  var data = input3.valueAsDate;
  if (!data) alert("infore uma data!");
  return data;
}
wizard.onFinaliza = function () {
  alert("Wizard Finalizado!");
}
.hide {
  display: none;
}
<div data-wizard="">
  <div data-step="Titulo 1">
    <label>
      Input 1:
      <input id="input1" type="text" required/>
    </label>
  </div>
  <div data-step="Titulo 2">
    <label>
      Input 2:
      <input id="input2" type="number" required/>
    </label>
  </div>
  <div data-step="Titulo 3">
    <label>
      Input 3:
      <input id="input3" type="date" required/>
    </label>
  </div>
</div>

<template id="tmplStep">
  <div class="step hide">
    <div class="title">
      
    </div>
    <div class="content">
      
    </div>
    <div class="actions">
      <input class="prev" type="button" value="Anterior" />
      <input class="next" type="button" value="Proximo" />
    </div>
  </div>
</template>
    
22.05.2016 / 02:57