Spring MVC and Ajax

Asked

Viewed 4,788 times

2

I am developing a web app in java using the Spring MVC framework. However, a part of the project has arrived that I am breaking my head!.

Before I will show the screen print to facilitate understanding.

Imagery: inserir a descrição da imagem aqui

What I want to do is this: when the user selects the company, the following elements (salary, 13th salary and income report) will be dynamically charged. Not all companies have all the options. Obs: That button generate demo will no longer exist.

After some research I thought of doing so:

  • Create a javascript function to take the select action and send the selected item via ajax. As I understand it, I can use Annotation @RequestBody to read the json sent by ajax.

  • With the selected item in hand, I can pass it as a parameter in the select I do in the bank that brings the options available to that company.

  • with the Annotation @ResponseBody sending one json with options and create tags with javascript.

I don’t know if the logic is right, but the problem with that is that I can’t do.

My doubts:

  • How do I retrieve the selected item and send it to the spring controller? For example: I have a company object that has the name and CNPJ attributes, as I take the CNPJ attribute of the selected item and send to controller?

  • Then as I return to the page the items available to that company?

Check out my controller’s method:

@RequestMapping(value = "/", method = RequestMethod.GET)
public String index(Model model, 
                    @ModelAttribute("usuarioLogado") Usuario usuario,
                    Empresa empresa) {

    Funcionario funcionario = daoFuncionario.getFuncionario(usuario);
    List<Empresa> empresas = daoEmpresa.listaEmpresas(funcionario);

    model.addAttribute("empresa", empresa);
    model.addAttribute("funcionario", funcionario);
    model.addAttribute("empresas", empresas);

    return "usuario/menu";
}

Now the page:

<form:form commandName="empresa" class="form-horizontal">
    <fieldset>
        <legend>Olá, ${funcionario.nome}</legend>

        <!-- Select Empresa -->
        <div class="form-group">
            <label for="empresa">Empresa </label>

            <!-- 
            <select class="form-control">
                <c:forEach items="${empresas}" var="empresa">
                    <option value="${empresa.cnpj}">
                        ${empresa.cnpjFormatado}- ${empresa.razaoSocial}
                    </option>
                </c:forEach>
            </select>
             -->

            <form:select path="razaoSocial" class="form-control">
                <form:options items="${empresas}" />
            </form:select>

I also don’t know the best way to select. The first way using c:forEach or using spring select itself.

EDIT

Guys, I’m getting the selected item in the controller like this:

$(document).ready(function() {

    function empresaSelecionaClick() {
        var cnpj = $("#razaoSocial option:selected" ).val();
        var json = {cnpj : cnpj}

        $.ajax({
            type: 'POST',
            url: 'menuDinamico',
            contentType: 'application/json',
            data: JSON.stringify(json),

            success : function(resposta){
                // pegar a lista e montar os elementos
            }
        })

        .done(function() {
            console.log("success");
        })
        .fail(function() {
            console.log("error");
        });
    }

    $("#razaoSocial").change(empresaSelecionaClick);

});

Methods in the controller:

@RequestMapping(value = "/menu", method = RequestMethod.GET)
public @ResponseBody ModelAndView menu(@ModelAttribute("usuarioLogado") Usuario usuario, Empresa empresa) {
    ModelAndView mav = new ModelAndView("usuario/menu");

    Funcionario funcionario = daoFuncionario.getFuncionario(usuario);
    mav.addObject("funcionario", funcionario);
    mav.addObject("empresa", empresa);
    mav.addObject("empresas", daoEmpresa.listaEmpresas(funcionario));

    return mav;

}

@RequestMapping(value = "/menuDinamico", method = RequestMethod.POST)
private @ResponseBody List<UltimoPeriodoAberto> listaOpcoes(@RequestBody Empresa empresa) {

    List<UltimoPeriodoAberto> opcoes = daoUltimoPeriodoAberto.getPeriodoHolerite(empresa);

    System.out.println("CNPJ empresa: " + empresa.getCnpj());

    for (UltimoPeriodoAberto ultimoPeriodoAberto : opcoes) {
        System.out.println(ultimoPeriodoAberto.getDescricao());
    }

    return opcoes;
}

But in the second method is giving null Pointer execption on the line:

List<UltimoPeriodoAberto> opcoes = daoUltimoPeriodoAberto.getPeriodoHolerite(empresa);

I can’t identify the mistake.

EDIT

Well, I did it. I did it like this

I changed the note @RequestBody for @ModelAttribute:

    @RequestMapping(value = "/menuDinamico", method = RequestMethod.POST)
public @ResponseBody List<UltimoPeriodoAberto> opcoesDinamicas(@ModelAttribute("empresa") Empresa empresa) {

    System.out.println(empresa.getCnpj());
    List<UltimoPeriodoAberto> opcoes = daoUltimoPeriodoAberto.getPeriodoHolerite(empresa);

    System.out.println(opcoes.size());

    return opcoes;
}

I changed the ajax request too..

$(document).ready(function() {
    function empresaSelecionaClick() {
        var empresa = $(this).serialize();

        $.ajax({
            type: 'POST',
            url: 'menuDinamico',
            data: empresa,
        })

        .done(function(data) {
            console.log("success");
            console.log(data)
        })
        .fail(function() {
            console.log("error");
        });
    }
    $("#cnpj").change(empresaSelecionaClick);
});

Now I’m getting the bug 406 in ajax:

The Resource identified by this request is only capable of generating Responses with Characteristics not acceptable According to the request "Accept" headers

ajax is not getting my list, anyone has any suggestions?

  • Felipe, in your query you will pass what as parameter for query? ID, CNPJ? Expect as a result a JSON, for example? If it is, I make an example for you, just let me know the version of Spring is using.

  • I am passing as parameter the CNPJ. The query I am able to do, I believe that my problem is in receiving the return of the ajax request. For example, I should return a list of objects, and assemble the tags with the attributes. I am using version 4.1.6 of spring.

2 answers

2


The way you intend to use it, and the way I understand it, you don’t need to use it @ModelAttribute, since we will not send the whole object of the model that is in the form.

As we will pass only the CNPJ as a parameter of the consultation, I will consider two examples:

  • receiving the CNPJ in the body of the request, as JSON, in the format {"cnpj": "11111111111"}. The service for consultation will meet in /empresa/menudinamico
  • receiving CNPJ as the URL parameter, with the URL as the default ``

In the above scenarios, both return JSON as a response and this is my test HTML:

<select id="razaoSocial">
  <option value="0">--- Selecione</option>
  <option value="11111111111">Empresa 1</option>
  <option value="22222222222">Empresa 2</option>
</select>

<div id="options"></div>

Okay, after this introduction, first I will show you how the controller looked. For the first case we will have this (note that it is in summary form):

@ResponseBody
@RequestMapping(value = "/menudinamico",
    method = {GET, POST},
    consumes = APPLICATION_JSON,
    produces = APPLICATION_JSON)
public final List<UltimoPeriodoAberto> optionByRequestBody(@RequestBody final Empresa empresa) {
    LOGGER.info("CNPJ recebido: " + empresa.getCnpj());
    return this.daoUltimoPeriodoAberto.getPeriodoHolerite(empresa);
}

As we are passing a JSON in the body of the message we need to talk to spring this, ie with this it will take the JSON and already deserialize to the object.

And for the second, your method should look like this:

@ResponseBody
@RequestMapping(value = "/menudinamico/{cnpj}", method = GET, produces = APPLICATION_JSON)
public final List<UltimoPeriodoAberto> optionByRequestParam(@PathVariable final String cnpj) {
    LOGGER.info("CNPJ recebido: " + cnpj);
    return this.daoUltimoPeriodoAberto.getPeriodoHolerite(new Empresa(cnpj));
}

In this case, unlike the first, we are using @PathVariable, which is to tell the string to "inject" the value according to the template of the given URL. It has also been removed @RequestBody since we are not sending anything in the message body now, but only as a parameter in the URL.

In both chaos we use @ResponseBody, that is, we are talking about spring to serialize the return according to the defined in produce and according to the Accept sent by the customer as well. You can see about this in detail in the reference documentation.

For the call, I will consider this excerpt that you said is already OK to recover the value of the form’s select by changing only their body:

$(document).ready(function() {
    function empresaSelecionaClick() {
        var cnpj = $(this).val();
        var json = {cnpj: cnpj};

        // substitua aqui conforme necessário

    }

    $("#razaoSocial").change(empresaSelecionaClick);
});

This for the first method:

$.ajax({
    type: 'POST',
    url: '/menudinamico',
    contentType: 'application/json',
    data: JSON.stringify(json),
    success : function(data){
        innerOptionsInHTML(data);
    }
}).done(function() {
    console.log("success");
}).fail(function() {
    console.log("error");
});

This for the second method:

$.ajax({
    type: 'GET',
    url: '/menudinamico/' + cnpj,
    success : function(data){
        innerOptionsInHTML(data);
    }
}).done(function() {
    console.log("success");
}).fail(function() {
    console.log("error");
});

I used the CNPJ 11111111111 for the first method, which returned me this:

[
   {
      "id":1,
      "nome":"Option 1"
   },
   {
      "id":2,
      "nome":"Option 2"
   }
]

And the CNPJ 22222222222222 for the second method, which returns this:

[
   {
      "id":3,
      "nome":"Option 3"
   },
   {
      "id":4,
      "nome":"Option 3"
   },
   {
      "id":5,
      "nome":"Option 5"
   }
]

To generate content in your HTML, you can do something like this:

function innerOptionsInHTML(data) {
    var json = JSON.parse(data);
    var options = $("#options");
    $(options).empty();
    $.each(json, function(i, item) {
        var obj = json[i];
        $(options).append("<li id=" + obj.id + ">" + obj.nome + "</li");
    });
};

After the execution of the first method, it generated this part in HTML for the first case (the second is similar):

<ul id="options">
    <li id="1">Option 1</li>
    <li id="2">Option 2</li>
</ul>

Observing: how are you using ModelAndView, it is important to generate the options within the form and with name/id to the object, so that Spring can "inject" it correctly.

Observation 2: I am considering that your environment is with Spring properly configured.

  • Thank you very much. I understand very well your explanation. I was in trouble because I had set up Jsonviewresolver, contentNegotiatingViewResolver and configureContentNegotiation, to return more than one format, but it will no longer be necessary. I had with you with the modelAttribute, but in this context I also understand that it is better to use Pathvariable, because I do not send the whole object.

  • @Nice feliperenan you got

0

I’d do it this way:

A call AJAX with the event .change(); That is, every time an item is selected within my select would be passed the company name or id of the same to my controller, with this you could perform the logic in your action returning the relevant values and later mounting them on the screen with the jQuery.

Example:

<form:select id="razao-social" path="razaoSocial" class="form-control">
        <form:options items="${empresas}" />
</form:select>

$('#razao-social').change(function() {
    $.get("action/fazalgumaCoisa", {parametro: $(this).val()}, function(data) {
        //Criar a montagem da tela com o jQuery
    }).fail(function() {
        alert("Ocorreu um erro, tente novamente mais tarde.");
    });
});


    @RequestMapping(value = "action/faz-alguma-coisa", method=RequestMethod.GET)
    public @ResponseBody Object retornaAlgo(Model model, 
                    @ModelAttribute("usuarioLogado") Usuario usuario,
                    Empresa empresa, @RequestParam Long parametro){
   model.addAttribute("empresa", empresa);
    model.addAttribute("funcionario", funcionario);
    model.addAttribute("empresas", empresas);
        return Object;      
    }

In your case you must return something to be treated with the callback of Ajax.

  • Oops... thanks for the suggestion. But I tried to do it this way and I couldn’t. I can even pick up a selected item and send it to the controller, but I can’t do the rest. With the selected item I have to select in the database and return a list to the page. But when I do this null Pointer execption. I will edit the question to facilitate understanding

  • nullpionter stack where? Debug and check what is being returned in the options list?

  • João, I added Exception to the post. I debug yes, but I can’t identify the error. I declared the objects with Autowired, and the funny thing is that if I create a list within the "menu" method and Percorro does not give the Exception, the same code within the other method is giving null Pointer.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.