Build input dynamically from View Model in an ajax response - javascript

I have a controller action that sends down a collection of ApiViewModel Types. Each view model represents a different API that can be executed server-side, and output the response in the browser through an ajax call using jquery. The server generates the HTML so all I have to do is insert the server-side HTML into the current page.
Some of the APIs can only execute if they are given some parameters. I'm trying to do this in a generic fashion. When the user clicks the run button, I display a model Bootstrap dialog. Within this dialog I'd like to provide the input options for the parameters on the API selected.
This is my HTML for the modal dialog
<div class="modal fade"
id="appParameters"
role="dialog"
aria-labelledby="appParametersLabel">
<div class="modal-dialog"
role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title" id="appParametersLabel"></h4>
</div>
<div class="modal-body" id="appDialogBody">
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
i have enough information to send to the server, letting the server know what API is going to be executed, and what View Model needs to go with it. What I'm not sure of though is how should I put together the HTML on the server side, so that I can send the HTML to the client and have the MVC validation attributes still work for client-side validation?
The javascript I'm using to send the data to the server, and add the servers HTML to the DOM is this. If no View Model is required, I just make a request to the server to execute the app and output the server-side response HTML. I think I don't need to do anything in regards to my Java Script below to handle the validation stuff; not sure though.
$('.btn-success').click(function () {
var button = $(this);
var appId = $(this).data("app");
var vmRequired = $(this).data("vm-required");
if (!vmRequired) {
var url = "/Home/RunApp?appId=" + appId;
$.get(url, function (data) {
$("div[data-app='" + appId + "']").html(data);
var buttonColumn = button.parent();
var appRow = buttonColumn.parent();
var hiddenRow = appRow.next()
hiddenRow.removeClass("hidden");
appRow.click(function () {
var hiddenColumn = hiddenRow.children().first();
var resultsDiv = hiddenColumn.children().first();
resultsDiv.empty();
hiddenRow.addClass("hidden");
$(this).off();
hiddenRow.off();
})
hiddenRow.click(function () {
var hiddenColumn = $(this).children().first();
var resultsDiv = hiddenColumn.children().first();
resultsDiv.empty();
$(this).addClass("hidden");
appRow.off();
$(this).off();
})
});
return;
}
var appName = $(this).data("app-name");
$('#appParametersLabel').html(appName);
$('#appParameters').modal({
keyboard: true,
backdrop: "static",
show: false,
}).on('show', function () {
$.get(url, function (data) {
$('#appDialogBody').html(data);
})
});
});
Do I just generate the HTML on the server side, like I would normally in the view? When the HTML is inserted into the DOM, will the validation all work correctly while using unobtrusive jquery validation?

Related

AJAX incorrectly GETs and POSTs multiple data

I am working on a feature of my webapp that allows a user to add a place to a list. The current path is to open a modal which shows the user's lists and allows them to choose which list to add the data to.
The problem I have is two fold:
For some reason when the user click's a list it tries to send the data multiple times depending on how many lists there are (eg. if there are 2 lists it tries to send the data to the chosen list twice.) This is causing all sorts of headaches at my database.
The modal doesn't seem to reset or something meaning every time I reopen the modal 2 more lists are added meaning there are all these duplicates (example).
I have tried adding and removing event handlers etc. but I can't seem to debug the issue myself. Thank you in advance for any support you can offer.
Here is the JS function
const addToListModal = function(venueName) {
$('#userlist-modal').modal('show');
$.ajax({
type: 'GET',
url: '/api/userlist/',
data: {
'username': userName
},
success: function(data) {
data.forEach(item => {
var listName = item.list_name;
var listId = item.id;
var listItem = $("#userListsModal").append(
`<li class="userlistModal" id="${listName}" data-name="${listName}" data-pk="${listId}">
${listName}
</li>`)
$(listItem).on('click', function(e) {
if (e.target && e.target.matches("li.userlistModal")) {
var listname = e.target.getAttribute('data-name');
var listId = e.target.getAttribute('data-pk');
addVenueToList(listId, venueName);
e.preventDefault();
}
})
})
}
});
};
AJAX Post:
const addVenueToList = function(listId, venue) {
$.ajax({
type: "POST",
url: '/api/uservenue/',
dataType: 'json',
data: {
csrfmiddlewaretoken: document.querySelector('input[name="csrfmiddlewaretoken"]').value,
'user_list': listId,
'venue': venue
},
success: function(data) {
console.log('User added: ' + data)
},
});
}
And here is the pertinent HTML
<div class="modal" id="userlist-modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content rounded-0">
<div class="modal-header">
<h5 class="modal-title" id="ModalLabel"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div id="lists-column" class="mt-1 ml-2 mb-1 col-2" style="height: 367px;">
<ul id="userListsModal" class="list-group list-group-flush" style="width: 250px">
{% csrf_token %}
{{ form.as_p }}
</ul>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
$(listItem).on('click', function(e) { …
listItem is not what you think it is here.
You did
var listItem = $("#userListsModal").append(`<li class="userlistModal" …
before - but that does not return a reference to the newly appended item, it returns the jQuery object you started with (that is necessary, for method chaining to work).
So what you are doing here, is adding a click handler for #userListsModal. And since this happens in a loop over your list items, you are adding this same click handler twice (here in this situation), resp. as many times, as you have list items.
Your check
if (e.target && e.target.matches("li.userlistModal")) {
inside the callback function still works - because when you click on any of the list items, the event bubbles up the DOM tree. You are handling it when it reaches #userListsModal, but the event target is still the list item you clicked on.
You need to either add these event handlers to the individual new list items you just appended, or – probably better in a case like this – use event delegation, set up one handler for this, outside of the loop that creates the list items.
Try using jQuery's one() method: one
So, instead: $(listItem).on('click', function(e) {...})
Do this: $(listItem).one('click', function(e) {...})
I think the problem here is that the list (of lists) is not cleared every time you open the modal dialog. So every time the same items are appended and new 'click' event handlers are added. Try to call jQuery empty() method before running AJAX '/api/userlist' (it will remove all event handlers as well)
const addToListModal = function(venueName) {
$("#userListsModal").empty();
$('#userlist-modal').modal('show');
$.ajax({
// ...

Django modal forms with ajax tables

I am using modal forms with django ajax tables:
https://pypi.org/project/django-bootstrap-modal-forms/
https://pypi.org/project/django-ajax-tables/
How can I update data asychronously by the modal form?
Here is some example code:
Registered Views:
def index(request):
return render(request, 'proto/index.html')
class BookTableView(View):
model = Books
my_table = BooksTable
def get(self, request):
data = self.model.objects.all()
#filtering
table = self.my_table(data)
RequestConfig(request, paginate = {"per_page": 6, "paginator_class": LazyPaginator}).configure(table)
return HttpResponse(table.as_html(request))
class BookUpdateView(BSModalUpdateView):
model = Books
template_name = 'proto/books/update_book.html'
form_class = BookModelForm
success_message = 'Success: Book was updated.'
success_url = reverse_lazy('index')
Table:
class BooksTable(tables.Table):
column1 = tables.TemplateColumn(verbose_name='Read',
template_name='proto/columns/column1.html',
orderable=False)
column2 = tables.TemplateColumn(verbose_name='Update',
template_name='proto/columns/column2.html',
orderable=False)
class Meta:
model = Books
Column2 html template button
<button type="button" class="update-book btn btn-sm btn-primary" data-form-url="{% url 'update_book' record.id %}" onclick="updateBookModalForm()">
<span class="fa fa-pencil"></span>
Close update buttons on update_book.html modal form
<button type="button" class="close" data-dismiss="modal" aria-label="Close" onclick="update_books_id('', '/proto/books')">
<span aria-hidden="true">×</span>
</button>
...
<div class="modal-footer">
<button type="button" class="submit-btn btn btn-primary">Update</button>
</div>
Calling ajax tables on index.html and javascript for modals :
...
<div class="col-12 mb-3">
{% ajax_table "books_id" "books" %}
</div>
<script>
function updateBookModalForm() {
$(".update-book").each(function () {
$(this).modalForm({
formURL: $(this).data("form-url"),
asyncUpdate: true,
asyncSettings: {
closeOnSubmit: false,
successMessage: asyncSuccessMessageUpdate,
dataUrl: "books/",
dataElementId: "#books-table",
dataKey: "table",
addModalFormFunction: updateBookModalForm
}
});
});
}
updateBookModalForm();
</script>
Surprisingly this works and appears assyncronouse on the frontend even not adding a books/ url, but I get a Not Found proto/books on terminal as expected. My question is how to make the update asynchronous on the ajax table without redirecting to the homepage. I really have tried a lot of things with the javascript function, but any modifications I make, mostly taking things out makes it break the update. Really just making the update is enough, what i want is no redirection after the update or any advice on what is the best way to go from here.
Thank you for your time.
reivaJ

Javascript Issue with aria-hidden popup

I have this ajax code that gets a res.json(event) from the server and then creates an object based on the value received.
Here is part of that code:
html += `<div class="card-header" id="headingOne-${i}">` +
`<div class="event-time"><time class="published" datetime="2017-03-24T18:18">${data[i].events.targetReminder} | ${data[i].events.targetAmPM}</time><div class="more"> <svg class="olymp-three-dots-icon"><use xlink:href="svg-icons/sprites/icons.svg#olymp-three-dots-icon"></use> </svg><ul class="more-dropdown"><li>Mark as Completed</li> <li>Delete Event </li></ul></div></div>` +
`<h5 class="mb-0 title"><a href="" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne" >${data[i].events.title}<i class="fa fa-angle-down" aria-hidden="true"></i>` +
` <span class="event-status-icon" data-toggle="modal" data-target="#public-event"><svg class="olymp-calendar-icon" data-toggle="tooltip" data-placement="top" id ="uncomplet-${i}"data-original-title="UNCOMPLETED"><use xlink:href="svg-icons/sprites/icons.svg#olymp-calendar-icon"></use></svg></span></a></h5></div>` +
`<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#headingOne-${i}"><div class="card-body" id="${data[i].events._id}" onClick="reply_click()">${data[i].events.caption}</div><div class="place inline-items"><svg class="olymp-add-a-place-icon"><use xlink:href="svg-icons/sprites/icons.svg#olymp-add-a-place-icon"></use></svg><span>${data[i].events.location}</span></div></div></div>`;
And here is the output of the code:
The aria-hidden that I'm talking about is this one
<div class="modal fade" id="public-event" tabindex="-1" role="dialog" aria-labelledby="public-event" aria-hidden="true">
What I have tried and already know it's not a proper solution is here
Code test
I created a script that change aria-hidden => true or false but I don't know how to link it with my box
<script type="text/javascript">
function reply_click()
{
document.getElementById('public-event').setAttribute('aria-hidden', 'false');
}
</script>
another failed try:
<script type="text/javascript">
function reply_click()
{
alert('this function is called')
$(`#mark-${i}`).onclick = function() {
document.getElementById('public-event').setAttribute('aria-hidden', 'false');
};
}
</script>
I also added in the HTML this function onClick="reply_click()" but nothing is happening. I only get the alert('this function is called')
Could you suggest me an idea, please?
By looking at your question and discussion on the comment section I think that you are trying to add click event on your dynamically generated div section(html part) and open the pop up modal.
We can achieve that by using $('#id' OR '.class').modal('show') in jquery.
So why don't you add a class any where inside your div section of html and bind a click function using jquery. Suppose you have added a class name showModal on the very first div after card-header like, div="card-header showModal"
$('#eventCard').on('click', '.showModal', function(){
$('#public-event').modal('show');
});
We cannot directly use $('.showModal').click...... because document structure is change after appending the html section after #eventCard which was initially was not present. Hpe this works.
could this be your problem ? you create an item dynamically with javascript, but the item you want to select, click event, is not actually created at that time. Once the item you want to export is created, you can select it and want to make the changes you want.
udpated: I added a snippet of what I meant. in order for me to select the H1 tag that occurs after I click on the button, I have to write a function that will occur after it occurs.
const button = document.querySelector(".clickme");
const container = document.querySelector(".container");
button.addEventListener("click", () => {
container.innerHTML += `
<h1 class = "change-modal"> ı cant select this</h1>
<div class="modal fade" id="public-event" tabindex="-1" role="dialog" aria-labelledby="public-event"
aria-hidden="true"> `
})
const changeModal = document.querySelector(".change-modal")
console.log(changeModal)
<button class="clickme">Click me</button>
<div class="container" style="background-color: red;">
</div>
<h1 class="change-modal">ı select this</h1>
updated2 :
for (let i = 0; i < data.length; i++) {
html += // your html
$("eventCard").append(html);
//after this, you can select the item and type the function because the items are created here.
}
Updated 3!!! : I explained how to do things with javascript without using jquery. Hopefully you know what I mean.
async getData() { // fetch operations using javascript
const data = await fetch(url); // your api url
const jsonToData = await data.json(); // here you can Request api and obtain the data
return jsonToData;
}
getData().then((data) => {
console.log(data) // ıts probably an array.
for (let data = 0; data < jsonToData.length; data++) {
// data operations, what if you want to
html += // you printed document items,
}
})
.then(() => {
//!!! IMPORTANT!!! this is where you need to perform the operation of selecting the element function. you can also write a function that can work for the code here, but I've written it one by one for now.
const clickedElement = //type whatever element you want to click on. !!
clickedElement.addEventListener("click", function() {
const elementToChange = document.getElementById(".public-event");
elementToChange.setAttribute('aria-hidden', 'false');
})
})
.catch(err => console.log(err))

Add Friend with Ajax - Django

I'm using Django-Friends
I'm trying to have it so when a user clicks on the add friend, the button disappears(or ideally says Request sent). However, when I click the button, it doesn't disappears. I am new at Django and Ajax, so I'm assuming that this is an error on my part. Most likely the HttpResponse.
That part actually confuses me a lot. The HttpResponse, render, render_to_response, etc. I know that I can use render or render_to_response when I want to load a template. But what if I don't want to load up a new template or go to a new page? Like I want to be able to complete an action like add a friend, or add a page, etc; all on one page. I know you can use ajax to do it, but I don't know the django technical aspect of it.
Anyway, here's my code. Right now, nothing happens. The button doesn't disappear, and there is no friendships request sent.
profile.html
<div class="text-center">
<div>
"{{currUserprofile.tagline}}"
</div>
{{currUser.profile.city}}, {{currUser.profile.state}}
{{currUser.id}}
</div>
<!-- <button id="addfriend" data-profileid="{{currUser.id}}" class="btn btn-primary" type="button"> <span class="glyphicon glyphicon-plus"></span>
Request Friend</button>
--> <!--Find a way to signify looking or not looking to mentor -->
<button id="addfriend" data-profileid="{{currUser.id}}" class="btn btn-primary" type="button"> <span class="glyphicon glyphicon-plus"></span>
Request Friend</button>
ajax.js
$(document).ready(function () {
$('#addfriend').click(function () {
var profile_id = $(this).data("profileid");
$.get('/myapp/addfriend/id=' + profile_id, function (data) {
$('#addfriend').fadeOut();
});
});
})
views.py
#login_required
def profile(request, id):
context = RequestContext(request)
currUser = User.objects.get(pk = id)
profile = UserProfile.objects.filter(user = currUser)
return render_to_response('myapp/profile.html', {'currUser': currUser, 'UserProfile': UserProfile}, context)
#login_required
def addfriend(request, id):
context = RequestContext(request)
other_user = User.objects.get(pk=id)
new_relationship = Friend.objects.add_friend(request.user, other_user)
profile = UserProfile.objects.filter(user = other_user)
return HttpResponse(new_relationship)
Here is a working JSFiddle, but you can't post data {profile_id: profile_id}with a getyou should use a postor add the data as params, as I did:
HTML:
<button id="addfriend" data-profileid="{{currUser.id}}" class="btn btn-primary" type="button"> <span class="glyphicon glyphicon-plus"></span>
Request Friend</button>
JS:
$(document).ready(function () {
$('#addfriend').click(function () {
var profile_id = $(this).data("profileid");
$.get('/myapp/addfriend/?profile_id=' + profile_id, function (data) {
$('#addfriend').fadeOut();
});
});
});

jQuery - dynamically loading ASP.NET partial view into modal

Question background:
I have an MVC project where I am attempting to create a partial view modal content that is then concatenated to the rest of its respective modal markup and then finally appending to a 'main' modal div.
The code:
Main modal div:
<div class="modal fade"
id="basicModal"
tabindex="-1"
role="dialog"
aria-labelledby="basicModal"
aria-hidden="true">
</div>
JQuery to trigger the modal popup along with the AddModal method to build the modal content:
<script>
$(document).ready(function () {
$(".btn").click(function () {
AddModal();
$("#basicModal").modal("show");
});
});
</script>
AddModal method to build the modal:
AddModal = function() {
var partialHtml = $(#Html.Partial("../Modal/Index", new { id = 1 }))
$html = $('<div class="modal-dialog">' +
'<div class="modal-content">' +
'<div class="modal-header">' +
'<button type="button" class="close" data-dismiss="modal" aria-hidden="true">X</button>' +
'<h4 class="modal-title" id="myModalLabel">Modal title</h4>' +
'</div>' +
'<div class="modal-body">'+partialHtml+'</div>' +
'<div class="modal-footer">' +
'<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>' +
'<button type="button" class="btn btn-primary">Save changes</button>' +
'</div>' +
'</div>' +
'</div>');
$("#basicModal").append($html);
};
Partial View:
<h4>Test Partial view</h4>
The issue:
I am running into an error of Uncaught SyntaxError: Unexpected token < which is being caused by the HTML Markup of the partial view as shown:
var partialHtml = $(<h4>Test Partial view</h4>)
How can I successfully escape this forward slash so that the HTML from my partial view is correctly added to the rest of the dynamically added markup?
Instead of using Html.Partial() and storing that in a JavaScript string, consider using this technique: Render Partial View Using jQuery in ASP.NET MVC
Basically, in your AddModal() method, fire off a GET request that hits an action that returns the partial view as HTML. Then, just replace the contents of #basicModal with the returned HTML:
AddModal = function() {
var partialHtml;
var url = '../Modal/Index?id=1';
$.get(url, function(data) {
$("#basicModal").html(data);
});
};
I've used this technique to load modals before and it works well. However, one problem with doing it this way is that $.get() is an async call, so .modal("show") is probably going to fire before the data has been fetched. We can solve this by having AddModal return the promise generated by $.get():
AddModal = function() {
var partialHtml;
var url = '../Modal/Index?id=1';
return $.get(url, function(data) {
$("#basicModal").html(data);
});
};
Then you would change your calling line to:
AddModal().done(function() {
$("#basicModal").modal("show");
});

Categories

Resources