Message Boards

[Question]How to get element from clicked button in a table.

Tiago Machado, modified 5 Years ago.

[Question]How to get element from clicked button in a table.

Junior Member Posts: 59 Join Date: 2/21/19 Recent Posts
Hello! i'm new here and developing in Liferay, i'm pretty much enjoying it and training but i'm having some trouble with a thing, and i have 2 other minor questions too if you may help me i will be grateful!
The 1st Big one question is:
I have an entity called Movie, created  a form that allows me to persist the data, and created a jsp with a table that show me all the movies in the table, and i have 2 extra columns for "Edit" and "Delete" <-Buttons. 
The problem is that in my MoviesPortlet i'm always receiving the same MovieId! the idea is for example: i have 3 rows, i click on row 3 delete button and i want the MovieID from that row so i can retrieve it in the portlet and delete the movie from the table, but i'm always receiving the first row MovieID.

My JSP Code:
​​​​​​​&lt;%-- RENDER AND ACTIONS URLS HERE --%&gt;
&lt;%
PortletURL deleteMovieURL = renderResponse.createActionURL();
deleteMovieURL.setParameter(ActionRequest.ACTION_NAME, "deleteMovie");
%&gt;
&lt;%-- DELETE MOVIE ACTION --%&gt;
<portlet:actionurl var="deleteMovie">
    <portlet:param name="jspPage" value="/view.jsp" />
</portlet:actionurl>

&lt;%-- GO BACK RENDER URL--%&gt;
<portlet:renderurl var="goBackURL">
    <portlet:param name="jspPage" value="/view.jsp" />
</portlet:renderurl>

&lt;%----------------------------------%&gt;

<h1 align="center">Movies List</h1>

&lt;%
    int count = MovieLocalServiceUtil.getMoviesCount();
    List<movie> movies = MovieLocalServiceUtil.getMovies(0, count);
%&gt;
<aui:form name="fm" method="POST" action="<%=deleteMovieURL.toString()%>">
    <table class="aui" align="center" border="1">
        <thead>
            <tr align="center">
                <th id="movieIdT">MovieId</th>
                <th id="movieName">Movie</th>
                <th id="description">Description</th>
                <th id="rating">Rating</th>
                <th id="action" colspan="2">Action</th>
            </tr>
        </thead>
&lt;%
    for (Movie movie : movies) {
%&gt;
        <tbody>
            <tr align="center">
                <td><aui:input name="movieId" value="<%=movie.getMovieId() %>"></aui:input></td>
                <td>&lt;%= movie.getMovieName() %&gt;</td>
                <td>&lt;%= movie.getDescription() %&gt;</td>
                <td>&lt;%= movie.getRating() %&gt;</td>
                <td>Edit</td>
                <td><aui:button type="submit" name="btnId" value="Delete"></aui:button></td>
            </tr>
        </tbody>
&lt;%
    }
%&gt;
    </table>
</aui:form>

<br> <a href="<%=goBackURL%>">Go Back »</a>[b][/b]​​​​​​​</movie>
​​​​​​​The idea is when all is set, hidding the ID's. I know i probably need to add "onclick="" " but i already had, and tried a lot of thing and nothing happened. all i can do is retrieve de ID from the  aui:input.... And since i'm here, let me ask you, for the Edit Button i want to redirect to another JSP with the form for editing that Movie, so i need to add a onclick too and pass a link right?

Oh, and i dont know JS, so im using Java scripts there, and i would like to continue doing it if possible until i feed confortable with this. (but if theres other way on doing this please tell me.)

Now, my MoviesPortlet.java code:
​​​​​​​public void deleteMovie(ActionRequest actionRequest, ActionResponse actionResponse)&nbsp;
&nbsp;&nbsp; &nbsp;throws IOException, PortalException {
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Long movieId = ParamUtil.getLong(actionRequest, "movieId");
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;System.out.println(movieId);
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;//MovieLocalServiceUtil.deleteMovie(movieId);
&nbsp;&nbsp; &nbsp;}
​​​​​​​
And thats all for the my big issue here!

Now the other 2 things i wanted to ask... probably noobie thing...sorry...
first of all, since service builder builded my impl and persistence layer, why do i need to add methods to addMovie or deleteMovie for example? to add a movie i had to set the movie properties, and i had already done it in the impl....all i wanted to know if what im doing is correct or did i miss something...
The second is just about the layout of the table. looks pretty meh... and since im not good with front end i searched and found this: https://docs.atlassian.com/aui/7.9.9/docs/tables.html

a layout like that is cute enough, but all i get is...well... the html default table...


Sorry for the long post! Hope you can help me here! thanks a lot!
thumbnail
David H Nebinger, modified 5 Years ago.

RE: [Question]How to get element from clicked button in a table.

Liferay Legend Posts: 14914 Join Date: 9/2/06 Recent Posts
Tiago Machado
I have an entity called Movie, created  a form that allows me to persist the data, and created a jsp with a table that show me all the movies in the table, and i have 2 extra columns for "Edit" and "Delete" <-Buttons. 
The problem is that in my MoviesPortlet i'm always receiving the same MovieId! the idea is for example: i have 3 rows, i click on row 3 delete button and i want the MovieID from that row so i can retrieve it in the portlet and delete the movie from the table, but i'm always receiving the first row MovieID.


To do this in JSP, I'd approach your button urls similar to how Liferay builds them in the guestbook: https://github.com/liferay/liferay-docs/blob/7.1.x/develop/tutorials/code/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbookwebportlet/view.jsp 


Tiago Machado
For the Edit Button i want to redirect to another JSP with the form for editing that Movie, so i need to add a onclick too and pass a link right?
Well, as in the link I shared, a render URL will take you to another page but you could make it an action url to invoke your backend. In either case, the <aui:button /> tag's onClick attribute just uses the generated URLs.




Tiago Machado
first of all, since service builder builded my impl and persistence layer, why do i need to add methods to addMovie or deleteMovie for example? to add a movie i had to set the movie properties, and i had already done it in the impl....all i wanted to know if what im doing is correct or did i miss something...
I'm not sure what you're asking here...

Liferay normally recommends building out a verbose addXxx() method to your service, ala https://github.com/liferay/liferay-docs/blob/7.1.x/develop/tutorials/code/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookLocalServiceImpl.java#L56

The reason to do a method like this is that it further encapsulates the process of building an entity.

For example, to do an entity in the service layer you invoke createXxx(), then set the fields, then addXxx() to persist the entity. Creating this method in the service layer centralizes your business logic inside of the add so there is one place to maintain it. Also, in case you had multiple points in your UI code where an Xxx can be added, a centralized method helps reduce the code duplication of actually completing the add.

If you mean the addXxx() and deleteXxx() action request methods, you need the handler for the server side to be the entry point to complete the activity; how it does it is up to you, but the form handling details will always be part of that. Some sort of "automagic" handling like Struts used to do is simply too prone to security issues, so the manual extraction of parameters is normal.

So I typically extract the parameter values in the action handler, do interim validation and verification, and finish by invoking the service method to handle the persistence.


Tiago Machado
The second is just about the layout of the table. looks pretty meh... and since im not good with front end i searched and found this: https://docs.atlassian.com/aui/7.9.9/docs/tables.html

a layout like that is cute enough, but all i get is...well... the html default table...
The look and feel is going to be handled by classes on your tags more than anything else.

For example, my raw table tag is typically something like:

<br><table class="table table-bordered table-hover table-striped">These classes will do more to style your table than using the &lt;aui:table /&gt; tag does.</table>
Tiago Machado, modified 5 Years ago.

RE: [Question]How to get element from clicked button in a table.

Junior Member Posts: 59 Join Date: 2/21/19 Recent Posts
David H Nebinger
Tiago Machado
I have an entity called Movie, created  a form that allows me to persist the data, and created a jsp with a table that show me all the movies in the table, and i have 2 extra columns for "Edit" and "Delete" <-Buttons. 
The problem is that in my MoviesPortlet i'm always receiving the same MovieId! the idea is for example: i have 3 rows, i click on row 3 delete button and i want the MovieID from that row so i can retrieve it in the portlet and delete the movie from the table, but i'm always receiving the first row MovieID.


To do this in JSP, I'd approach your button urls similar to how Liferay builds them in the guestbook: https://github.com/liferay/liferay-docs/blob/7.1.x/develop/tutorials/code/com-liferay-docs-guestbook/modules/guestbook/guestbook-web/src/main/resources/META-INF/resources/guestbookwebportlet/view.jsp 


Tiago Machado
For the Edit Button i want to redirect to another JSP with the form for editing that Movie, so i need to add a onclick too and pass a link right?
Well, as in the link I shared, a render URL will take you to another page but you could make it an action url to invoke your backend. In either case, the <aui:button /> tag's onClick attribute just uses the generated URLs.




Tiago Machado
first of all, since service builder builded my impl and persistence layer, why do i need to add methods to addMovie or deleteMovie for example? to add a movie i had to set the movie properties, and i had already done it in the impl....all i wanted to know if what im doing is correct or did i miss something...
I'm not sure what you're asking here...

Liferay normally recommends building out a verbose addXxx() method to your service, ala https://github.com/liferay/liferay-docs/blob/7.1.x/develop/tutorials/code/com-liferay-docs-guestbook/modules/guestbook/guestbook-service/src/main/java/com/liferay/docs/guestbook/service/impl/GuestbookLocalServiceImpl.java#L56

The reason to do a method like this is that it further encapsulates the process of building an entity.

For example, to do an entity in the service layer you invoke createXxx(), then set the fields, then addXxx() to persist the entity. Creating this method in the service layer centralizes your business logic inside of the add so there is one place to maintain it. Also, in case you had multiple points in your UI code where an Xxx can be added, a centralized method helps reduce the code duplication of actually completing the add.

If you mean the addXxx() and deleteXxx() action request methods, you need the handler for the server side to be the entry point to complete the activity; how it does it is up to you, but the form handling details will always be part of that. Some sort of "automagic" handling like Struts used to do is simply too prone to security issues, so the manual extraction of parameters is normal.

So I typically extract the parameter values in the action handler, do interim validation and verification, and finish by invoking the service method to handle the persistence.


Tiago Machado
The second is just about the layout of the table. looks pretty meh... and since im not good with front end i searched and found this: https://docs.atlassian.com/aui/7.9.9/docs/tables.html

a layout like that is cute enough, but all i get is...well... the html default table...
The look and feel is going to be handled by classes on your tags more than anything else.

For example, my raw table tag is typically something like:

<br><br><br><br><br><br><br><table class="table table-bordered table-hover table-striped">These classes will do more to style your table than using the &lt;aui:table /&gt; tag does.Thank you very much for your answer!I have understood the last 2 questions i had, and thank you for that. But for the main issue im not getting it.I had a look to the code and i dont think im getting it, for example :</table><table><tbody><tr></tr><tr><td>List&lt;Guestbook&gt; guestbooks = GuestbookLocalServiceUtil.getGuestbooks(scopeGroupId);</td></tr><tr><td></td></tr></tbody></table><br>where did that scopeGroupId came from? i have to do ....getMovies(0, count);<br><br>The JSP i have looked into looks like are depending on other ones. the id is getting passed from page to page? i dont really want to be depending on other JSP's<br>I want to encapsulate my JSP, every one doing his job. for example my JSP for adding a movie:<pre><code>​​​​​​​<h1>Add / Edit Form</h1>

&lt;%
&nbsp;&nbsp; &nbsp;PortletURL updateMovieURL = renderResponse.createActionURL();
&nbsp;&nbsp; &nbsp;updateMovieURL.setParameter(ActionRequest.ACTION_NAME, "addMovie");
%&gt;

<aui:form name="fm" method="POST" action="<%=updateMovieURL.toString() %>">
&nbsp;&nbsp; &nbsp;<aui:input name="movieName" label="Movie Name" />
&nbsp;&nbsp; &nbsp;<aui:input name="description" />
&nbsp;&nbsp; &nbsp;<aui:input name="rating" />
&nbsp;&nbsp; &nbsp;<aui:button type="submit" value="Save" />
</aui:form>

<portlet:renderurl var="goBackURL">
&nbsp;&nbsp; &nbsp;<portlet:param name="jspPage" value="/view.jsp" />
</portlet:renderurl>
<br> <a href="<%=goBackURL%>">Go Back »</a></code></pre>​​​​​​​And then, in my addMovie method i just get the data, create a new movie object, set the properties and persist them. easy enough.<br><br>In my JSP where im listing the&nbsp; movies in a table like the code shown above, i just want to grab de MovieID from a corresponding movie ID from the row where i click, and then in my deleteMovie method, get that movieID and delete de movie. I'm getting a ID, but it's always the first movie ID. in the view are fields have theyr ID's corresponding to the movie, so when i click i should get that ID from the aui:input name="movieId", i'm missing some logic here. im gonna try to get this some...<br><br>If you could give me some light i would apreciate it. well, at least this is my idea, im kinda new to JSP's, i felt like that code is a bit complicated and depending too much, or it is a better practice?
thumbnail
David H Nebinger, modified 5 Years ago.

RE: [Question]How to get element from clicked button in a table.

Liferay Legend Posts: 14914 Join Date: 9/2/06 Recent Posts
The guestbook example has the guestbooks scoped by site, so different sites would have different list of guestbooks. The scopeGroupID is the current group (site) ID that the user is viewing.

Encapsulation and dependence are not different sides of same coin. Encapsulation means that you have bound together all related code into a class and hidden the unnecessary details; it doesn't mean they must be completely standalone. All of Liferay, all of Spring, heck even a portion of the JDK leverage encapsulation to pull related code together yet they are still free to depend upon other classes.

That's the nature of good design and it is how Liferay structures all of its JSPs. Where you see dependence, I see encapsulation and the right separation of concerns.

> And then, in my addMovie method i just get the data, create a new movie object, set the properties and persist them. easy enough.

Well, it may seem easy enough, but it is very sloppy in that details of adding the movie are spread all over.  The action method shouldn't know the details of having to call createMovie() with the correct ID, followed by field setting for created/last update details (audit details), company and/or group ids for scoping records, ...  But you have bled that over. Maybe not so much of an issue for this simple Movie entity, but things quickly get out of hand as soon as you want to add indexing, asset support, workflow, recycle bin, ...

That is why Liferay code is so fully encapsulated and detached. It is simply a better and more effective way to encapsulate business details and minimizing maintenance impact.

Sure it is not as easy as just throwing it all into your addMovie() action handler in the portlet, but your simple code would not pass my code review by a long shot.

Back to your JSP, you are getting the same movie ID in the delete handler because the individual delete buttons have the same URL; there is no ID assignment going on, you create the URL at the top of the JSP and reuse it for every <aui:button /> without change.

In the example I shared, Liferay builds each URL inside of the loop so each URL is different:
<portlet:renderurl var="viewPageURL">
  <portlet:param name="mvcPath" value="/guestbookwebportlet/view.jsp" />
  <portlet:param name="guestbookId" value="<%=String.valueOf(curGuestbook.getGuestbookId())%>" />
</portlet:renderurl>

<aui:nav-item cssClass="<%=cssClass%>" href="<%=viewPageURL%>" label="<%=HtmlUtil.escape(curGuestbook.getName())%>" />

​​​​​​​
Tiago Machado, modified 5 Years ago.

RE: [Question]How to get element from clicked button in a table.

Junior Member Posts: 59 Join Date: 2/21/19 Recent Posts
Thank you for your answer david! you are indeed very helpful, and dont take me wrong please, maybe i expressed myself bad, i was just thinking and asking something, if i offended you by any way i apologize.

Okay! I'm already coding to a better way in my addMovie method! that why i asked if I was doing it right since the service builder constructed those layers, and when I implemented I was already setting the properties, so it didn't make any sense creating the object again and setting everything again. but now I understood the way to do it, and the way to delete the movie from id.

I cant use Thymeleaf right?

Just one more thing, I saw that in the guestbook tutorial, that is in dev.liferay that was not used service builder am I right? and the method was directly implemented in the guestbookPortlet, and it was created one other model called Entry to save the Entries of the guestbook so for example in the portal if i had 2 guestbooks, the 2 of them will have different entries right? it's good practice to do it like that? or just a choice to our needs?
in the backend course, Eddie leaves the "main" portlet empty, and creates classes for each action/render command. for example, creates a class AddXxxMVCActionCommad and a EditXxxMVCRenderCommand, the action one extends baseMVCActionCommand and puts code in the doProcessAction method, and the render one implements MVCRenderCommand and code is added in the render method.

is it better to extend baseMVCActionCommand or implement MVCActionCommand?
or can i just create the methods in the portlet that is created like the guestbookPortlet?

Sorry, I just want to have an idea what to do and what to not do so i gain better practices.
​​​​​​​thanks a lot!
thumbnail
David H Nebinger, modified 5 Years ago.

RE: [Question]How to get element from clicked button in a table.

Liferay Legend Posts: 14914 Join Date: 9/2/06 Recent Posts
So actually I think you can use Thymeleaf, although I haven't done so myself. You'd be building a spring MVC portlet war and including all necessary thymeleaf stuff.

Guestbook example definitely uses ServiceBuilder, it is part of the whole tutorial.  There they have two entities, the GuestBook which represents a book, and an Entry which represents an individual guestbook entry.  The GuestBook is scoped at group (site) level so different sites can set up their own GuestBook for collecting entries. This has nothing to do with being a good practice or not, it has to do with requirements. In the guestbook example, the idea was that sites could maintain their own guestbooks. Whether it makes sense for your project also depends upon requirements; if you're building an IMDB clone, you probably don't need to scope it that way, but if your building a list of movie recommendations that separate sites might take advantage of, then maybe it does make sense.

We will normally extend BaseMVCActionCommand because that way there's boilerplate code that you can take advantage of.

The old way was to create methods inside of the Portlet class. That way still works, but it is not extensible. For example, if you wanted to change how Liferay adds a bookmark in the bookmark portlet. If the method to handle the action is in the portlet class, there is no way to do that without having a full portlet class replacement. With it in a separate component class, then all you need to do is create your own component with a higher service ranking.

In my work, I always do the separate action/render classes because I expect that clients may want to do an override. But if I were an internal developer with total control over the code, the project, etc then the value of separating the code ends up being more a question of finesse and design purety. If you had to make a change to how a movie is added, you just make the change in the portlet and republish. No big deal.

But, if you're part of a team, you benefit from the separate classes as it may avoid having to deal with code merges, etc. It also tends to be a cleaner design than if it is all mashed together.
Tiago Machado, modified 5 Years ago.

RE: [Question]How to get element from clicked button in a table.

Junior Member Posts: 59 Join Date: 2/21/19 Recent Posts
Okay David, got it!

Thank you very much for spending your time clarifying my questions!
Best wishes David!