Accessing dynamic element id's in AngularJS - javascript

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.

Related

Using ajax to dynamically load modal details

I currently have a foreach in one of my blades. I want to create a button on each entry that will prompt a modal to show with some further details. I have the modal working correctly using many different methods (I had to try multiple due to the ensuing problem) but couldn't get a small feature I wanted to work.
// this is inside the foreach. isStaff is a custom attribute.
#if(auth::check() && auth::user()->isstaff)
<span style="position: absolute;top: 0;left: 0;" class="quickview btn btn-sm btn-primary ml-3" data-toggle="modal" data-target="#modal" value="{{ $loop->index }}"> // loop used to get index of foreach
<i class="fas fa-search"></i>
</span>
#endif
I basically want to have two arrows on the top of the modal (pictured) that would either go to the previous or next entry in the foreach.
image (sorry can't embed yet)
I'm wondering how i can handle this logic? at first, I passed in the id through my controller for the specified entry the incremented and decremented it, though i cannot pass the collection through.
$(document).ready(function(e) {
$('.quickview').on('click', function(e) {
e.preventDefault();
$('#modal').find(".modal-body").load("admin/preview/" + $(this).attr('value'));
});
});
Basically, is there a way to pass either the collection through, or dynamically change the ids in the modal then showing it? I had tried a var collection = "<?php json_encode($collection); ?>"; but i could no longer use relationships.
I had also tried an ajax request where I passed in the collection but a similar problem of not having relationships occurred.
I had thought about simply making the new URLs ex. admin/preview/n +1 and n - 1, though realistically I still need the collection in order to do $collection[n] to display
Sorry for the noob-ish question, thank you for reading and for any help in advance!

Django Modelmultiplechoicefield Checkboxselectmultiple Problem Getting Selected Checkbox

I have been working all day to try and get the selected values on one set of checkboxes on my Django form and copy the selected ones only to an identical checkbox on my form. If a user clicks on a checkbox in form.author defined below, I want the same identical checkbox to automatically be checked on form.favorite_author defined below.
I've tried a JQuery approach as documented here...Copy Selected Checkbox Value From One Checkbox To Another Immediately Using JQUERY but no luck so far. I've recently begun exploring an avenue with the Modelmultiplechoicefield and the checkboxselectmultiple parameters within the form itself. From what I can tell, when I am using JQuery and trying to check a box on the form, the value is coming back as undefined which is most likely why it is not propagating the checkbox selection to the other checkbox.
Here is my form....
class ManagerUpdateNewAssociateForm(forms.ModelForm):
class Meta:
model = Library
self.fields['author'] = forms.ModelMultipleChoiceField(
widget=forms.CheckboxSelectMultiple(),
queryset=Books.objects.all()
self.fields['favorite_author'] = forms.ModelMultipleChoiceField(
widget=forms.CheckboxSelectMultiple(),
queryset=Books.objects.all()
My HTML...
<div class="spacer83">
{{ form.author }}
</div>
<div class="spacer83">
{{ form.favorite_author }}
</div>
When I tried to trace JQuery, it told me the checkbox selections are undefined. I did read a bit about how Modelmultiplechoicefield, since it uses a queryset it doesn't show the selections, but I can't figure out how to get it to.
Thanks in advance for any thoughts.
In combination with the other issue included in this one, I went back to the JQuery route and explored further. From what I can tell, I was not able to use the class for the input because of the way the HTML for Django forms is generated in this use case. I ultimately was able to leverage the input name and then used the code below to interrogate the checkboxes accordingly:
$(document).ready(function() {
$("[name=author]").change(function() {
let selectedValA = $(this).val();
let isAChecked = $(this).prop("checked");
$(`[name=favorite_author][value="${selectedValA}"]`).prop("checked", isAChecked);
});
});
});

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

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.

Vue.js weird rendering with class binding

So basically I have an html table with a v-for that loops over an array of objects, nothing complicated here. When the user clicks on a row, it toggles the selected property of the object "linked" to the given row.
Now, I also setup a simple class-binding with v-bind:class="{'active': n.selected}" where n is my object, but it doesnt update. Now what is more weird is that I use the webpack template from the vue-cli, and when I :
Select a bunch of rows (no active class binded)
update code
hit F5 which triggers the webpack hot-reload
The selected rows suddenly get the active class, as the CSS says so, until the webpack hot-reload has regenerated the page.
Here is my code :
<tr
v-for="n in node.children"
track-by="id"
v-on:click="toggleElement(n)"
v-bind:class="{'active': n.selected}">
<td>
<i class="icons">
<i class="folder yellow icon"></i>
</i>
</td>
<td><a v-on:click.stop="getLevel(n.id)">{{ n.name }}</a></td>
<td><span v-if="n.modification_date">{{ n.modification_date | moment "calendar"}}</span><span v-else>-</span></td>
<td><span v-if="n.size">{{ n.size }}</span><span v-else>-</span></td>
And for the javascript method
toggleElement: function(element) {
element.selected = !!(element.selected === undefined || !element.selected);
},
Now some more details, the objects are being retrieved by an ajax call, and the selected property doesnt exist from the start.
Any advice or solution ?
Thanks a lot!
by default,vue.js can not track the property you add after vue instance had been created
BUT,there is some method you can call to tell vue that new properties been added:
vm.$set('selected', true) //this will make "selected" propertie
trackable
First, do not forget to add the closing </tr> in your code.
Second, you can not add new attributes on objects after creating them with VueJS.
If when creating the element, its attribute selected was undefined, then VueJS will not add the getter/setter on it and changing it won't update the interface.
You will need to add the selected attribute before passing it to VueJS.

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

Categories

Resources