Stimulus: how to handle repeating items with the same target name - javascript

I have a list of items, and each one has a link to click to edit it. I am using stimulus to make the edit "modal" form visible when they click that edit link. The id of what is going to be edited is present as an id= on the corresponding link tag of the list
So, the edit link looks like this:
<td>
<a data-action="click->content#edit"
data-target="content.editBtn"
id="<%= url_for(content) %>")>
Edit
</a>
</td>
And the idea is that the content#edit action in the stimulus controller examines the and locates the id of it and uses that to edit the right row.
However the problem I am having is, I think, that as a result all the rows of this list have a data-target with the same name and the wrong one (the first one?) gets bound to the target..
However if I wanted to make each data-target different by e.g. appending the id to it, now I have a long list of targets in the controller.js so that doesn't make sense.
What's the right way to handle?

If you're using Rails as the backend like your other questions seem to indicate, there may be a simpler, non-Stimulus solution. To use Stimulus, you'd need to fetch the data for the item from the server or from the DOM, display it in a form, then submit the correct form with the correct ID to the server through JavaScript. Why not just have a remote link_to button to the edit action for each item? Rails gets a JS request to the edit controller action, and you can load the modal form with the data that you have from your Ruby object.
If you use Stimulus for anything on the form, I'd use this opportunity to craft a Stimulus controller that listens to the ajax->send/error/complete events and automatically disables/enables buttons, sets loading spinners on buttons, and closes the modal. Those would be good areas to sprinkle in some functionality that Stimulus makes very simple.

This is actually a good use of Stimulus since it is modular. You add a controller for each row instead of having the controller around the page or table.
<tr data-controller="content">
<td>
<a data-action="click->content#edit" data-target="content.editBtn" id="<%= url_for(content) %>")>
Edit
</a>
</td>
</tr>

I was just having a similar problem.
This helped:
https://codepen.io/smashingmag/pen/ExPprPG
Basically you can loop over the targets like:
for(let tgt of this.mySameNameTargets) {
tgt.innerHTML = "Some Value"
}
This assuming you have this in the controller:
state targets = ["mySameName"]
You can also use "Action Parameters" to put the id in each row:
https://stimulus.hotwired.dev/reference/actions#action-parameters
From those docs it looks like this:
<div data-controller="item spinner">
<button data-action="item#upvote spinner#start"
data-item-id-param="12345"
data-item-url-param="/votes"
data-item-payload-param='{"value":"1234567"}'
data-item-active-param="true">…</button>
</div>
// ItemController
upvote(event) {
// { id: 12345, url: "/votes", active: true, payload: { value: 1234567 } }
console.log(event.params)
}
You can set the id of the item in that row in a param, and then when they click on that row you can dig it out of the params.

Related

Passing Javascript variable to route in laravel 5.3

I'm having a Laravel blade form which includes a table wihch displays some data from the database. And when i click on a certain column i wrote a js function to catch that id of the certain selected item to a js variable "var selectedItem".
Now i wanna pass this js variable to the 'edit.item.blade' page and load the relevant record corresponding to this value.
My question is what is the best way to edit a selected item in laravel ? and is there anyway to pass this JS variable to a route at a button click event and load the 'edit.item.blade' using the relevant record to edit.
What you usually do in laravel is pass the id of the record you want to see in the url. Say you want to view the details of a report with the id of 1 you'd go to the url "/reports/1" which points to a show function in the reports controller.
Routes
In your routes/web.php you'd add:
Route::get('/reports/{report}',RecordController#show);
What this is does is take anything typed after /reports/ and pass it to the show function. So if you'd go to /reports/1 the route would pass 1 to the show function
Controller
In your controller you have to make a show function which accepts the variable passed by your route. You'd then take that variable to look up the corresponding record and pass it along to a view.
Which would look like this
public function show($id){
$report = Report::find($id); // Find the corresponding report
// Pass the report along to the view resources/views/reports/show.blade.php
return view('reports.show',compact($report));
}
Show view
In your show view you can now use $report to get any information from the report like $report->name, depending on your database.
Index
Now in the index view, the view you were talking about I presume, you loop over all records from some table. Since you haven't included any code in your post I'm just going to assume you loop over your data using a foreach loop. Using that loop we can give each record a link depending on their id.
Which would look a bit like this
<table>
<tr>
<td> Name </td>
<td> Edit </td>
</tr>
#foreach($reports as $report)
<tr>
<td> $report->name </td>
<td>Edit</td>
</tr>
#endforeach
</table>

Pass information to bootstrap modal from Angular.js controller

Simplified problem
I have a store. For a product to be included in the store there needs to be a shelf for it. So, to add a new product to the store the workflow is:
Add a shelf
Add product to that shelf
(The workflow can not be changed)
Realization
The shelf is realized by a row in a table, which in turn is controlled by an Angular.js controller (each shelf is an object in an array). To add an product the user selects "create product" in a drop-down menu that is present on each row. This will show an bootstrap modal where I have from a controller added a tab for each product that is possible to add (since each product needs configuration :) ) , then when the user presses a "create" button in the modal a JavaScript method is called interfacing a REST interface to add the product (the UI is updated by a Socket.io event send from the server when the product has been added successfully.
Problem
The JavaScript method (CreateProduct) needs to now what row (R) was affected as well as what tab (T) was selected so that the "onclick" method for the button is CreateProduct(R, T);
My current solution is pretty ugly imho, I have two global variables for R and T, then I use jQuery to capture show event and tab event from the modal, the link in the dropdown has a field "data-row-id" that is identifying the row
HTML (Jade) snippet from dropdown menu:
a(data-toggle="modal", href="#createProduct", data-row-id="{{row.RowID}}") Create Product
JavaScript:
var R = null;
$('#productModal').on('show.bs.modal', function(e) {
R = $(e.relatedTarget).data('row-id');
});
var T = null;
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
T = e.target.text;
});
I hope there is a better solution to this, I probably am just thinking a bit upsidedown due to inexperience with Angular.js , perhaps there is a way to pass these through the model? Perhaps add these to the modal controller, but then, how to pass the row? My dream would be something like this on the button code
button(type="button", class="btn btn-default", data-dismiss="modal", ng-click="storeCtrl.CreateProduct({{modalCtrl.shelf, modalCtrl.Product)") Create Product
I found a better way (at least I don't need to use jQuery) using ModalService
I created a CreateProductModalController having a variable selectedProduct, this is set on a ng-click event in the tab
ul(class="nav nav-pills", id="tabContent")
li(ng-repeat="prod in products", ng-class="{active: $index == 0}", ng-click="activateTab(prod.name)")
a(href="#{{prod.name}}", data-toggle="tab") {{prod.name}}
The ModalService is called with the rowID that was clicked.
The only problem I have now is that all must be in $scope, I want it to be more encapsulated

Need to check value of hidden input field related to clicked element (multiple with same name)

Title isn't that clear, so let me see if I can explain what I'm doing.
I'm listing off users' posts, and have a like/comment button with those posts.
What I need to do, is capture when the like button is clicked (<span> tags), and then grab the post id from the hidden input field, and use that to post to the PHP script.
The PHP is doing all of the checking for if they're friends, privacy level is correct, etc. before actually submitting the like to the database, but I am currently just having the javascript/jquery be generated when the post is shown (naming each js variable/DOM element according to post id), but that's not very efficient and looks messy when viewing the source (But, it's the only way I can get it to work).
I want to be able to use an external javascript file to check when just the like button is clicked, and know what post that is being liked, and work that way.
I've been looking into this for quite some time, and it's to my understanding that this might work, but I have had no luck. I'm generating multiple posts on one page using foreach() loop, so the names/ids/classes of the elements are the same.
For a little better understanding, here's an example of what a post might look like:
<div class="feedPost">
<img src="#" class="feedProfile"/>
FirstName LastName
<div class="feedPostBody">Hello, world!</div>
<input type="hidden" value="24772" name="feedPostID">
<span class="feedLikeButton">Like</span> | Comment | 2 mins ago
</div>
and, using javascript/jquery, I want to be able to do something like this in an external js file:
$('.feedLikeButton').on('click',function(){
var post_id = 0; //I need to get the ID from the post that the like button is related to.
//If I just did $('.feedPostID').val() it wouldn't work
$.post("https://mysite/path/to/like.php", {post: post_id}).done(function(data){
if(data == "success"){
//This will set text from "Like" to "Unlike"
//Again, I can't just do $('.feedLikeButton') to access
//I guess I could do this.innerHTML? Would still need to access feed post id
} else {
//Probably will just flash error to user if error, or something similar
}
});
});
You should get the like button
var likeButton = $(this);
Then get it's container
var container = likeButton.parent();
Then find the hidden field
var idInput = container.find('[name="feedPostID"]');
Then get it's value:
var id = idInput.val();
With all these references you can do whatever you want.

Accessing dynamic element id's in AngularJS

I am working with an app that has an ng-repeat that populates a navigation sidebar with a list of items from a Mongo DB. The ng-repeat also populates a series of option buttons for each item. A couple of these option buttons share a dynamic id for each iteration in the ng-repeat. What should be happening here is when I click on one of these buttons, it would change the button 'text' and display some additional options under the menu item and toggle back when clicked again.
Here is my code for these buttons:
<span>
<button ng-hide="highlightItem()" ng-click="showTopic()" ng-attr-id="{{ 'category' + subject._id }}" class="add-button"><i class="fa fa-chevron-down"></i></button>
<button ng-click="hideTopic()" ng-show="highlightItem()" ng-attr-id="{{ 'category' + subject._id }}" class="add-button"><i class="fa fa-chevron-up"></i></button>
</span>
The issue that I am having is that I cannot seem to figure out how to access that dynamic id in my controller. I have code in place that will change the button between the ng-show and ng-hide, but it does it for all iterations of ng-repeat.
This is currently how I am attempting to access the dynamic id. I am not getting any errors, but when I try to use this in my function it doesn't work.
$scope.subjectList = subjects.get({});
var topicButton = document.getElementById('topic' + $scope.subjectList._id);
I have also tried
var topicButton = document.getElementById('topic' + $scope.subject._id);
What is the best way to access the dynamic id in Angular/Javascript? I do not want to use jQuery with this if at all possible.
First and foremost, never manipulate the DOM within an angular controller! It is bad practice. Also, it is bad practice to evaluate methods in ngShow/ngHide.
If I understand you correctly, you're trying to get the subject_id for some reason when the button is clicked. Why can't you just pass back either the id or the entire subject to your method? Then your html would look something like this:
<span>
<button ngClick="toggleTopic(subject)" class="add-button">
<i class="fa" ng-class="{'fa-caret-down': subject.hidden, 'fa-caret-up': !subject.hidden}"></i>
</button>
</span>
Then in your controller you could write something like this:
$scope.toggleTopic = function(subject) {
subject.hidden = !subject.hidden;
};
Using the hidden attribute of your subjects, you can now show or hide elements of your dropdown with ngShow/ngHide like so:
<p ng-bind="subject.descripton" ng-hide="subject.hidden"></p>
This way, you don't have to search the DOM for elements at all.

Emberjs show table cell after click on link

I have a table that is built from items in a DB and they contain sensitive information that I don't want to display until you click on a link contained in another table cell.
|item1|item2|click to show item3|(hidden span within cell)|
When you click on the link in cell 3 it will then show cell 4. I know how to accomplish this in typical jquery, but am not sure how to accomplish this in emberjs. Any ideas?
A JS fiddle of your setup would make this easier, but basically you would set a property on your controller from an action.
<span {{action showCell4}}>click to show item3</span>
on your controller have the showCell4 action:
actions: {
showCell4: function() {
this.set('cell4visible', true);
}
}
then for the table add a class binding
<td {{bind-attr class="cell4visible:visibleClassName:hiddenClassName">
sensitive info here
</td>

Categories

Resources