Flask-Admin Custom Select2 Ajax Field - javascript

I'm trying to extend a one-to-many field in my Flask-Admin app to use a custom Select2 Field. The javascript code for the field looks something like this:
function format(data) {
if (!data.id) return data.text; // optgroup
return "<img class='flag' src='" + data.text + "'/>" + data.id;
}
function formatSelection(data) {
return data.id;
}
$("#da2").select2({
maximumSelectionSize: 3,
formatResult: format,
formatSelection: formatSelection,
escapeMarkup: function(m) { return m; }
});
I am unsure of what I need to change in my view code. I've tried something like this:
class PostForm(wtf.Form):
title = fields.TextField('Title')
photos = fields.SelectField('Photo', widget=widgets.Select(multiple=True), id='da2')
class PostView(ModelView):
form = PostForm
def _feed_user_choices(self, mform):
photos = Photo.query.all()
mform.photos.choices = [(x.path, url_for('static',
filename=form.thumbgen_filename(x.path))) for x in photos]
return mform
def create_form(self):
form = super(Post2View, self).create_form()
return self._feed_user_choices(form)
but its not ajax and there is an error when trying to parse the list.
I feel I'm close, but need some guidance to get there, thanks for the help.

what you probably need is a lambda
def _feed_user_choices(self, mform):
mform.photos.choices = [(x.path, url_for('static',filename=form.thumbgen_filename(x.path))) for x in lambda: Photo.query.all()]
return mform

Related

loop posting ajax results until they are all posted

the below code works great for displaying in my first file
$.ajax({
url : "http://localhost/website/files/userstuff/files/",
asynch : false,
cache : false,
success: function (data) {
$(data).find("a").each(function(i, el) {
var val = $(el).attr('href');
if (val.match(/\.(pdf|doc|docx|txt|html|js|css|rar|7zip)$/)) {
var fileslocation = ("http://localhost/website/files/userstuff/files/" + val)
var displayfilestable = ("<table><thead><tr><th>Files</th></tr></table>");
var adddata = ("<tr><td><a href='"+ fileslocation +"'target='_blank'>"+ val +"</td></tr>");
$("#filestable").html(displayfilestable)
$("filestable, table").append(adddata);
console.log(adddata)
}
});
}
});
this code will as you would think pull and display the files in the table row, however it is only performing this for the first file it finds I was wondering if anyone here could help get this to display all of the files in the files folder in the table. thanks in advance
enter image description here
Your code just works fine. The problems is, in that loop (each) you keep re-create table. That why it show only 1 data. Check my example based on your code.
HTML
<div>
sad1.pdf<br>
sad2.pdf<br>
sad3.pdf
<div id="filestable"></div>
</div>
JAVASCRIPT
var displayfilestable = ("<table><thead><tr><th>Files</th></tr></table>");
$("#filestable").html(displayfilestable);
$("DIV").find("a").each(function(i, el) { // this is your data
var val = $(el).attr('href');
if (val.match(/\.(pdf|doc|docx|txt|html|js|css|rar|7zip)$/)) {
var fileslocation = ("http://localhost/website/files/userstuff/files/" + val)
var adddata = ("<tr><td><a href='"+ fileslocation +"'target='_blank'>"+ val +"</td></tr>");
$("filestable, table").append(adddata);
console.log(adddata)
}
});
AND Jsfiddle here :https://jsfiddle.net/synz/yrag1zpr/

Cascading Select Boxes in Admin

I have the following code to implement a cascading select boxes (as the field contract_mod is OneToOneField I can't use django-smart-selects or django-ajax-select).
When I'm creating a fieldset with the fields that I want to be shown, I have to put the contracts_from_selected in order to see the results in the admin interface (since contract_mod remain disabled after applying the code.).
fieldsets = [
[ None,
{
"fields" : [
("contracts_from_selected")
]
}
]
So I guess I should copy to another field the value of contracts_from_selected in order to be used in the fieldset.
Any suggestion?
models
class Person(models.Model):
name = models.CharField(max_length=20)
def __unicode__(self):
return self.name
def get_name(self):
return self.name
class Contract(models.Model):
person = models.ForeignKey(Person) #person hired
contract_mod = models.OneToOneField('self', blank = True, null = True)
contract_name = models.CharField(max_length=20) #just for testing
def __unicode__(self):
return self.get_name() + " " +self.contract_name
def get_name(self):
return self.person.get_name() #to make sure you get the person name in the admin
def contract_mod_name(self):
if self.contract_mod:
return self.contract_mod.contract_name
else:
return ""
admin
class SelectField(forms.ChoiceField):
def clean(self, value):
return value
class ContractForm(forms.ModelForm):
contracts_from_selected = SelectField()
class Meta:
model = Contract
widgets = { 'contract_mod' : forms.widgets.Select(attrs={'hidden' : 'true'}) }
class ContractAdmin(admin.ModelAdmin):
form = CForm
list_display = ('contract_name','get_name','contract_mod_name')#what you like
def save_model(self, request, obj, form, change):
if request.POST.get('contracts_from_selected'):
obj.contract_mod=Contract.objects.get(id=int(request.POST.get('contracts_from_selected')))
obj.save()
change_form
$(function () {
$("#id_person").change(function () {
var options = $("#id_contract_mod option").filter(function () {
return $(this).html().split(" ")[0] === $("#id_person option:selected").html();
}).clone();
$("#id_contracts_from_selected").empty();
$("#id_contracts_from_selected").append(options);
});
});

Populate list dynamically

Using jQuery FancyList from http://jquerywidgets.com/widgets/fancy-list/
I am trying to insert a <li> through code, not by a user click action.
My issue lies in the this keyword, specifically the following lines:
$(".fancy-list-footer .add").click( function() {
$('.tab-container .user-information .first-name:last').val('').removeClass('placeholder');
$('.tab-container .user-information .last-name:last').val('').removeClass('placeholder');
fancyList($(this));
});
I would like to be able to pass a first name and last name without having to populate the textboxes - a for loop will do this.
I tried changing the fancyList(elem) function to fancyList(elem, firstName, lastName) but I couldn't seem to get the correct value for elem - I tried var elem = document.getElementByClassName('fancy-list-footer') because I thought that's what the $(this) referred to in the button click, but this didn't work either.
Codepen: http://codepen.io/anon/pen/vEYaqa?editors=101
Any help appreciated!
You can make a function like so that will add a name:
function addToFancyList(first_name, last_name) {
$('.fancy-list').find(".fancy-list-content-list:last").append("<li><div class='person'><span class = 'first'>" + first_name + "</span><span class = 'last'>" + last_name + "</span></div><div class='clear'></div></li>");
}
And simply call it like so:
$(function () {
addToFancyList('Tom', 'Someone');
});
the answer relies in the function itself. here is the implementation of fancyList from the link you send:
function fancyList(elem) {
var this_parent = elem.parents(".fancy-list");
rel = this_parent.attr("rel");
var regex = /(<([^>]+)>)/ig;
first_name = this_parent.find(".fancy-list-footer input#fancy-list-first-name").val().replace(/[<\s]/g, "").replace(/[>\s]/g, "");
last_name = this_parent.find(".fancy-list-footer input#fancy-list-last-name").val().replace(/[<\s]/g, "").replace(/[>\s]/g, "");
// more lines of code...
but it can easly change to this:
function fancyList(elem, first_name, last_name ) {
var this_parent = elem.parents(".fancy-list");
rel = this_parent.attr("rel");
var regex = /(<([^>]+)>)/ig;
first_name = first_name .replace(/[<\s]/g, "").replace(/[>\s]/g, "");
last_name = last_name.replace(/[<\s]/g, "").replace(/[>\s]/g, "");
// more lines of code...
this way the function will better suit your needs and won't force you to generate elements just to get their textual values

How can I use an autosave partial view on a page with multiple forms?

Extending the example found at Autosave in MVC (ASP.NET), I wanted to create a partial to reuse in my application. I have one view with a tabbed layout, and each tab has its own form, and this is causing problems, namely that every form tries to submit every time, and only the first timestamp in the document updates. I understand why this is happening, but I don't know how I can fix it.
Partial's cshtml:
<div class="form-group">
<label class="control-label col-lg-2" for=""> </label>
<div class="col-lg-10">
<span class="help-block" id="autosaveTime">Not Autosaved</span>
</div>
</div>
#{
var autosaveString = "'" + #ViewData["autosaveController"] + "'";
if (ViewData["autosaveAction"] != null && ViewData["autosaveAction"] != "")
autosaveString += ", '" + ViewData["autosaveAction"] + "'";
}
<script type="text/javascript">
$(document).ready(function () {
autosave(#Html.Raw(autosaveString));
});
</script>
Javascript:
//methodName is optional-- will default to 'autosave'
function autosave(controllerName, methodName)
{
methodName = typeof methodName !== 'undefined' ? methodName : 'autosave'
var dirty = false;
$('input, textarea, select').keypress(function () {
dirty = true;
});
$('input, textarea, select').change(function () {
dirty = true;
});
window.setInterval(function () {
if (dirty == true) {
var form = $('form');
var data = form.serialize();
$.post('/' + controllerName + '/' + methodName, data, function () {
$('#autosaveTime').text("Autosaved at " + new Date);
})
.fail(function () {
$('#autosaveTime').text("There was a problem autosaving, check your internet connection and login status.");
});
dirty = false;
}
}, 30000); // 30 seconds
}
I have 2 ideas on how to fix it, but not sure which is more maintainable/workable:
Give each form an id, and pass that to the partial/autosave function. Add the name to the autosavetime text block for updates, and to determine which form to serialize/submit.
Somehow use jquery's closest function to find the form where the autosave block was placed, and use that to do what I was doing explicitly with #1.
First, make the URL using your Razor helper's Html extension (dynamically piecing URLs like this in JavaScript is unnecessarily risky). Take that, and stuff it in a data attribute on the tab control like so:
<div class="tab autosave" data-action-url='#Html.Action("Action", "Controller")'>
<form>
<!-- Insert content here -->
</form>
</div>
Then, you'll want something like this ONCE -- do not include it everywhere, and remove the javascript from your partial completely:
$(function() {
// Execute this only once, or you'll end up with multiple handlers... not good
$('.autosave').each(function() {
var $this = $(this),
$form = $this.find('form'),
dirty = false;
// Attach event handler to the tab, NOT the elements--more efficient, and it's always properly scoped
$this.on('change', 'input select textarea', function() {
dirty = true;
});
setInterval(function() {
if(dirty) {
// If your form is unobtrusive, you might be able to do something like: $form.trigger('submit'); instead of this ajax
$.ajax({
url : $this.data('action-url'),
data : $form.serialize()
}).success(function() {
alert("I'm awesome");
dirty = false;
});
}
}, 30 * 1000);
});
});

getjson() jquery dropdown not working

I have the following json file:
{
"viewport_size":
{"display_name":"VIEWPORT SIZE:",
"name":"viewport_size",
"format":"number",
"type":"dropdown",
"dflt":"640 * 480",
"values":["800*600","1280*720","1920*1080"],
"isMandatory":"true"},
"framerate":
{"display_name":"FRAMERATE:",
"name":"fps",
"format":"number",
"type":"dropdown",
"dflt":"30",
"values":["45","60","90"],
"isMandatory":"true"},
"pattern_resolution":
{ "display_name":"PATTERN RESOLUTION:",
"name":"resoln",
"format":"number",
"type":"dropdown",
"dflt":"8",
"values":["16","32","64"],
"isMandatory":"true"}
}
I am tryin to populate the dropdown list in my js file using getJSON()
var INPUT_TEXT='<input type="text"/>';
var INPUT_RADIO='<input type="radio"/>';
var INPUT_CHECKBOX='<input type="checkbox"/>';
var INPUT_DROPDOWN='<select id="items"></select>';
var SUBMIT_BUTTON='<input type="button" value="SUBMIT"/>';
var NEWLINE='<br></br>';
$.getJSON('json_input/client_settings_input.json',function(clientData)
{
$.each(clientData,function(i,feild)
{
if(this.type=="dropdown")
{
var html = '';
var len = feild.values.length;
//alert('lenght is'+len);
for (var i = 0; i< len; i++){
//alert('inside for');
html += '<option>'+ feild.values[i]+'</option>';
}
$('body #tabs #tabs-2 client').append (this.display_name).append(INPUT_DROPDOWN).append(html).append(NEWLINE);
}
});
$('body #tabs #tabs-2 #client').append(SUBMIT_BUTTON);
});
but I am not able to view the dropdown list and the values...Kindly point out where I am going wrong..
I want to populate like this VIEWPORT: dropdownlist values
FRAMERATE:dropdownlist values
PATTERN_RESOLUTION:dropdownlist values
Try
.append($(INPUT_DROPDOWN).html(html))
istead of .append(INPUT_DROPDOWN).append(html)
options must be in the select but you insert in the same level than the select.
see http://jsfiddle.net/q7fWt/
Try adding the parameter "jsoncallback=?" to your URL
$.getJSON('json_input/client_settings_input.json?jsoncallback=?',function(clientData)
jQuery will substitute the last questionmark, after the jsoncallback parameter, with an ID.
This ID will then be used on server side to create a response that will start with a function named from the ID value.
That would result in a response that would look something like this:
jQuery16205149872086476535_1314088378455({
"viewport_size":
{"display_name":"VIEWPORT SIZE:",
"name":"viewport_size",
"format":"number",
"type":"dropdown",
"dflt":"640 * 480",
"values":["800*600","1280*720","1920*1080"],
"isMandatory":"true"},
"framerate":
{"display_name":"FRAMERATE:",
"name":"fps",
"format":"number",
"type":"dropdown",
"dflt":"30",
"values":["45","60","90"],
"isMandatory":"true"},
"pattern_resolution":
{ "display_name":"PATTERN RESOLUTION:",
"name":"resoln",
"format":"number",
"type":"dropdown",
"dflt":"8",
"values":["16","32","64"],
"isMandatory":"true"}
});
So in a short answer, if your json response is NOT wrapped in this function name the callback function will not fire, instead you will get an error which you could see this way:
$.getJSON('json_input/client_settings_input.json?jsoncallback=?',function(clientData) {
//your code
}).error(function(jqXHR, textStatus, errorThrown) {
alert("Error: " + textStatus + " errorThrown: " + errorThrown);
})
Hope this helps
Patrik

Categories

Resources