I'm trying to pass my uploaded photo to javascript on a page, but I get this error. How can I fix it?
werkzeug.routing.BuildError
BuildError: ('uploaded_file', {'filename': u'user/user-1/scan.jpeg'}, None)
class AdminController(BaseController):
route_base = ''
route_prefix = '/admin'
trailing_slash = True
decorators = [login_required]
def __init__(self):
self.theme = "admin"
g.theme = self.theme
g.currentUser = g.auth.getUser()
self.viewData = {
"layout" : self.theme + "/" + "layouts/main.html"
}
class BaseMethodView(MethodView):
pass
class UserJsonDataController(AdminController, BaseMethodView):
def __init__(self):
super(UserJsonDataController, self).__init__()
def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
def get(self):
json = {}
users = User.select()
a = []
for user in users:
obj = {
"user_avatar":url_for("uploaded_file", filename = user.user_avatar)
}
a.append(obj)
json["rows"] = a
return flask.jsonify(json)
module.add_url_rule('/index/show', view_func=UserJsonDataController.as_view('show'))
$(document).ready(function () {
$("#grid").kendoGrid({
dataSource: {
transport: {
read: {
url: '{{ url_for("user_admin.show") }}',
dataType: "json"
}
},
schema: {
data: "rows"
}
},
columns: [{
field: "user_avatar",
title: "Profil",
template: "<img src='/#=user_avatar #' /> "
}
});
});
I know this is old but why not. #huseyin, you mentioned that your image is located in: app/upload/user/user-1/scan.jpeg. Your url_for() first argument should match that path and in your code it's 'uploaded_file'. Try changing it to 'upload'
Related
I'm trying to submit a form in a Rails 5.1 app that uses Vue.js and Dropzone. During the sendingEvent I am using JSON.stringify on the object before sending it over to the controller. However, I don't feel this is the correct way to do it, as I am then having problems with using strong params in the controller.
JS:
import Vue from 'vue/dist/vue.esm'
import VueResource from 'vue-resource'
import vue2Dropzone from 'vue2-dropzone'
Vue.use(VueResource)
document.addEventListener('DOMContentLoaded', () => {
if(document.getElementById('listing-multistep') !== null) {
Vue.http.headers.common['X-CSRF-Token'] = document.querySelector('input[name="authenticity_token"]').getAttribute('value');
var listingForm = document.getElementById('listing_form');
var listing = JSON.parse(listingForm.dataset.listing);
var locale = document.getElementsByTagName('html')[0].getAttribute('lang');
const myForm = new Vue({
el: '#listing-multistep',
components: {
vueDropzone: vue2Dropzone
},
data: function () {
return {
id: listing.id,
locale: locale,
slug: listing.slug,
activeStep: 0,
// More data
dropzoneOptions: {
url: `/${locale}/listings`,
method: 'post',
acceptedFiles: 'image/*',
uploadMultiple: true,
autoProcessQueue: false,
parallelUploads: 15,
maxFiles: 15,
addRemoveLinks: true,
thumbnailWidth: 150,
maxFilesize: 5,
dictDefaultMessage: "<i class='fa fa-cloud-upload'></i> Drop files here to upload (max. 15 files)",
headers: { 'X-CSRF-Token': Vue.http.headers.common['X-CSRF-Token'] }
}
}
},
methods: {
sendingEvent: function(file, xhr, formData) {
// This function gets called by Dropzone upon form submission.
var listingObj = this.setupListingObj()
formData.append('listing', JSON.stringify(listingObj))
},
listingRedirect: function(files, response) {
window.location = `/${this.locale}/listings/${response.slug}`
},
submitListing: function() {
var numFiles = this.$refs.listingDropzone.getAcceptedFiles().length
// If there are images to upload, use Dropzone
// Else submit the form normally.
if(numFiles > 0) {
this.$refs.listingDropzone.processQueue()
} else {
var listingObj = this.setupListingObj()
if(this.id === null) {
// POST if it's a new listing
this.$http.post(`/${this.locale}/listings`, {listing: listingObj}).then(
response => {
window.location = `/${this.locale}/listings/${response.body.slug}`
}, response => {
console.log(response)
})
} else {
// PUT if it's an existing listing
this.$http.put(`/${this.locale}/listings/${this.slug}`, {listing: listingObj}).then(
response => {
window.location = `/${this.locale}/listings/${response.body.slug}`
}, response => {
console.log(response)
})
}
}
},
setupListingObj: function() {
// do some processing...
var listingObj = {
id: this.id,
name: this.name,
// set more attributes
}
return listingObj
},
}
}
});
as you can see I am using formData.append('listing', JSON.stringify(listingObj)) on the sendingEvent.
My controller:
class ListingsController < ApplicationController
def create
#listing = Listing.new JSON.parse(params[:listing])
#listing.owner = current_user
respond_to do |format|
if #listing.save
format.html { redirect_to listing_path(#listing), notice: 'Listing was created successfully!' }
format.json { render :show, status: :created, location: #listing }
else
format.html { render :new }
format.json { render json: #listing.errors, status: :unprocessable_entity }
end
end
end
private
def listing_params
params.require(:listing).permit(
:name,
:bedrooms,
:beds,
:bathrooms,
:price_cents,
:price_currency,
:property_type,
:city,
:state,
:address,
:lat,
:lng,
:description,
:amenities => []
)
end
end
It seems to work in development, but when I run a test with this code in RSpec I get errors like:
Internal Server Error no implicit conversion of ActionController::Parameters into String
When I try to swap #listing = Listing.new JSON.parse(listing_params) it fails to work in development.
I have a feeling I'm not sending the form data across properly. What is the correct way to send the data over via Javascript to my Rails controller? Does it need to be strigified and then posted? How can I access it via strong params instead?
Thanks in advance!
UPDATE
This is what my spec looks like:
RSpec.feature 'Listing owners can create new listings' do
let(:owner) { create(:user, :owner) }
before do
login_as owner
visit new_listing_path
end
scenario 'successfully', js: true do
fill_in 'Property name', with: 'Example property'
select 'Apartment', from: 'Property type'
fill_in 'Address', with: 'Somewhere'
click_button 'Next'
fill_in 'Property description', with: Faker::Lorem.paragraph(2)
click_button 'Create Listing'
expect(page).to have_content 'Listing was created successfully!'
end
end
I am using Chrome headless for these tests in order to parse the Vue.js stuff on the form. In my rails_helper.rb I have:
require 'spec_helper'
require 'rspec/rails'
require 'capybara/rails'
require 'capybara/rspec'
require 'pundit/rspec'
require 'selenium/webdriver'
RSpec.configure do |config|
# ...
Capybara.javascript_driver = :headless_chrome
end
and I have a support/chrome_driver.rb file with the following:
Capybara.register_driver(:headless_chrome) do |app|
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
chromeOptions: { args: %w[headless disable-gpu] }
)
Capybara::Selenium::Driver.new(
app,
browser: :chrome,
desired_capabilities: capabilities
)
end
can you help me to fix my problem, I made gridview, and I use combobox to filter data in gridview and get gridview performcallback after filter, but gridview not callback after filter. how can I do ? Help me Please?
this is some code :
controller gridview filter :
[HttpPost]
public ActionResult FilterTypePro(String typePro)
{
//Session["typePro"] = typePro;
var model = Model._ProposalObject.ListDataProposal();
if (typePro != null && typePro != string.Empty)
{
model = Model._ProposalObject.ListDataProposal(typePro);
}
return PartialView("_gvPartialViewProposals", model);
}
and this is code to get value filter combobox :
function OnClickFilter(type) {
type = cbTypeProposal.GetValue();
$.ajax({
type: "POST",
cache: false,
async: false,
url: '#Url.Action("FilterTypePro", "App")',
data: { 'typePro': type },
success: function (data) {
/*gvPartialViewProposals.AdjustControl();
try {
gvPartialViewProposals.PerformCallback();
}
catch(er){
}*/
gvPartialViewProposals.Refresh(data);
}
});
}
and this is code to view cshtml :
groupItem.Items.Add(item =>
{
item.Caption = "Type Of Proposal";
item.Width = 400;
item.SetNestedContent(() =>
{
ViewContext.Writer.Write("<table><tr><td>");
Html.DevExpress().ComboBox(cmbSettings =>
{
cmbSettings.Name = "cbTypeProposal";
cmbSettings.Width = 100;
cmbSettings.Properties.DropDownStyle = DropDownStyle.DropDownList;
cmbSettings.ShowModelErrors = true;
cmbSettings.Properties.Items.Add("ATL", "ATL");
cmbSettings.Properties.Items.Add("BTL", "BTL");
cmbSettings.Properties.ClientSideEvents.SelectedIndexChanged = "function(s, e) { OnClickFilter();}";
}).Render();
ViewContext.Writer.Write("</td></tr></table>");
});
}); ;
}).GetHtml();
I hope you can help me.
Modify your javascript function as follows:
function OnClickFilter(type){
type = cbTypeProposal.GetValue();
gvPartialViewProposals.PerformCallback({
'typePro': type
});
}
Modify your grid settings as follows:
settings.Name = "gvPartialViewProposals";
settings.CustomActionRouteValues = new { Controller = "App", Action = "FilerTypePro" };
I have a jquery context menu on my landing page where I have hardcode menu items. Now I want to get the menu items from server. Basically the idea is to show file names in a specified directory in the context menu list and open that file when user clicks it...
This is so far I have reached..
***UPDATE***
C# code
[HttpPost]
public JsonResult GetHelpFiles()
{
List<Manuals> manuals = null;
var filesPath = Server.MapPath(#"\HelpManuals");
var standardPath = new DirectoryInfo(filesPath);
if (standardPath.GetFiles().Any())
{
manuals = standardPath.GetFiles().Select(x => new Manuals
{
Name = GetFileNamewithoutExtension(x.Name),
Path = x.Name
}).ToList();
}
return Json(manuals, JsonRequestBehavior.AllowGet);
}
private string GetFileNamewithoutExtension(string filename)
{
var extension = Path.GetExtension(filename);
return filename.Substring(0, filename.Length - extension.Length);
}
JavaScript Code
$.post("/Home/GetHelpFiles", function (data) {
$.contextMenu({
selector: '#helpIcon',
trigger: 'hover',
delay: 300,
build: function($trigger, e) {
var options = {
callback: function(key) {
window.open("/HelpManuals/" + key);
},
items: {}
};
$.each(data, function (item, index) {
console.log("display name:" + index.Name);
console.log("File Path:" + index.Path);
options.items[item.Value] = {
name: index.Name,
key: index.Path
}
});
}
});
});
Thanks to Matt. Now, the build function gets fire on hover.. but im getting illegal invocation... and when iterating through json result, index.Name and this.Name gives correct result. But item.Name doesn't give anything..
to add items to the context menu dynamically you need to make a couple changes
$.contextMenu({
selector: '#helpIcon',
trigger: 'hover',
delay: 300,
build: function($trigger, e){
var options = {
callback: function (key) {
var manual;
if (key == "adminComp") {
manual = "AdminCompanion.pdf";
} else {
manual = "TeacherCompanion.pdf";
}
window.open("/HelpManuals/" + manual);
},
items: {}
}
//how to populate from model
#foreach(var temp in Model.FileList){
<text>
options.items[temp.Value] = {
name: temp.Name,
icon: 'open'
}
</text>
}
//should be able to do an ajax call here but I believe this will be called
//every time the context is triggered which may cause performance issues
$.ajax({
url: '#Url.Action("Action", "Controller")',
type: 'get',
cache: false,
async: true,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (_result) {
if (_result.Success) {
$.each(_result, function(item, index){
options.items[item.Value] = {
name: item.Name,
icon: 'open'
}
});
}
});
return options;
}
});
so you use build and inside of that define options and put your callback in there. The items defined in there is empty and is populated in the build dynamically. We build our list off of what is passed through the model but I believe you can put the ajax call in the build like I have shown above. Hopefully this will get you on the right track at least.
I solved this problem the following way.
On a user-triggered right-click I return false in the build-function. This will prevent the context-menu from opening. Instead of opeing the context-menu I start an ajax-call to the server to get the contextMenu-entries.
When the ajax-call finishes successfully I create the items and save the items on the $trigger in a data-property.
After saving the menuItems in the data-property I open the context-menu manually.
When the build-function is executed again, I get the items from the data-property.
$.contextMenu({
build: function ($trigger, e)
{
// check if the menu-items have been saved in the previous call
if ($trigger.data("contextMenuItems") != null)
{
// get options from $trigger
var options = $trigger.data("contextMenuItems");
// clear $trigger.data("contextMenuItems"),
// so that menuitems are gotten next time user does a rightclick
// from the server again.
$trigger.data("contextMenuItems", null);
return options;
}
else
{
var options = {
callback: function (key)
{
alert(key);
},
items: {}
};
$.ajax({
url: "GetMenuItemsFromServer",
success: function (response, status, xhr)
{
// for each menu-item returned from the server
for (var i = 0; i < response.length; i++)
{
var ri = response[i];
// save the menu-item from the server in the options.items object
options.items[ri.id] = ri;
}
// save the options on the table-row;
$trigger.data("contextMenuItems", options);
// open the context-menu (reopen)
$trigger.contextMenu();
},
error: function (response, status, xhr)
{
if (xhr instanceof Error)
{
alert(xhr);
}
else
{
alert($($.parseHTML(response.responseText)).find("h2").text());
}
}
});
// This return false here is important
return false;
}
});
I have finally found a better solution after reading jquery context menu documentation, thoroughly..
C# CODE
public JsonResult GetHelpFiles()
{
List<Manuals> manuals = null;
var filesPath = Server.MapPath(#"\HelpManuals");
var standardPath = new DirectoryInfo(filesPath);
if (standardPath.GetFiles().Any())
{
manuals = standardPath.GetFiles().Select(x => new Manuals
{
Name = GetFileNamewithoutExtension(x.Name),
Path = x.Name
}).ToList();
}
return Json(manuals, JsonRequestBehavior.AllowGet);
}
HTML 5
<div id="dynamicMenu">
<menu id="html5menu" type="context" style="display: none"></menu>
</div>
JavaScript Code
$.post("/Home/GetHelpFiles", function (data) {
$.each(data, function (index, item) {
var e = '<command label="' + item.Name + '" id ="' + item.Path + '"></command>';
$("#html5menu").append(e);
});
$.contextMenu({
selector: '#helpIcon',
trigger: 'hover',
delay: 300,
items: $.contextMenu.fromMenu($('#html5menu'))
});
});
$("#dynamicMenu").on("click", "menu command", function () {
var link = $(this).attr('id');
window.open("/HelpManuals/" + link);
});
Here's my solution using deferred, important to know that this feature is supported for sub-menus only
$(function () {
$.contextMenu({
selector: '.SomeClass',
build: function ($trigger, e) {
var options = {
callback: function (key, options) {
// some call back
},
items: JSON.parse($trigger.attr('data-storage')) //this is initial static menu from HTML attribute you can use any static menu here
};
options.items['Reservations'] = {
name: $trigger.attr('data-reservations'),
icon: "checkmark",
items: loadItems($trigger) // this is AJAX loaded submenu
};
return options;
}
});
});
// Now this function loads submenu items in my case server responds with 'Reservations' object
var loadItems = function ($trigger) {
var dfd = jQuery.Deferred();
$.ajax({
type: "post",
url: "/ajax.php",
cache: false,
data: {
// request parameters are not importaint here use whatever you need to get data from your server
},
success: function (data) {
dfd.resolve(data.Reservations);
}
});
return dfd.promise();
};
I followed that tutorial about exporting Kendo Grid Data : http://www.kendoui.com/blogs/teamblog/posts/13-03-12/exporting_the_kendo_ui_grid_data_to_excel.aspx
Now I´m trying to export all data (not only the showed page) ... How can I do that?
I tried change the pagezise before get the data:
grid.dataSource.pageSize(grid.dataSource.total());
But with that my actual grid refresh with new pageSize. Is that a way to query kendo datasource without refresh the grid?
Thanks
A better solution is to generate an Excel file from the real data, not from the dataSource.
1]
In the html page, add
$('#export').click(function () {
var title = "EmployeeData";
var id = guid();
var filter = $("#grid").data("kendoGrid").dataSource._filter;
var data = {
filter: filter,
title: title,
guid: id
};
$.ajax({
url: '/Employee/Export',
type: "POST",
dataType: 'json',
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
success: function (result) {
window.location = kendo.format("{0}?title={1}&guid={2}", '/Employee/GetGeneratedExcel', title, id);
}
});
});
2]
Add a method "Export" to the controller:
[HttpPost]
public JsonResult Export(KendoGridFilter filter, string guid)
{
var gridRequest = new KendoGridRequest();
if (filter != null)
{
gridRequest.FilterObjectWrapper = filter.Filters != null ? filter.ToFilterObjectWrapper() : null;
gridRequest.Logic = filter.Logic;
}
var query = GetQueryable().AsNoTracking();
var results = query.FilterBy<Employee, EmployeeVM>(gridRequest);
using (var stream = new MemoryStream())
{
using (var excel = new ExcelPackage(stream))
{
excel.Workbook.Worksheets.Add("Employees");
var ws = excel.Workbook.Worksheets[1];
ws.Cells.LoadFromCollection(results);
ws.Cells.AutoFitColumns();
excel.Save();
Session[guid] = stream.ToArray();
return Json(new { success = true });
}
}
}
3]
Also add the method "GetGeneratedExcel" to the controller:
[HttpGet]
public FileResult GetGeneratedExcel(string title, string guid)
{
// Is there a spreadsheet stored in session?
if (Session[guid] == null)
{
throw new Exception(string.Format("{0} not found", title));
}
// Get the spreadsheet from session.
var file = Session[guid] as byte[];
string filename = string.Format("{0}.xlsx", title);
// Remove the spreadsheet from session.
Session.Remove(title);
// Return the spreadsheet.
Response.Buffer = true;
Response.AddHeader("Content-Disposition", string.Format("attachment; filename={0}", filename));
return File(file, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", filename);
}
Also see this project on github.
See this live example project where you can export the Employees to Excel. (Although this returns filtered data, but you can modify the code to ignore the kendo grid filter and always return all data.
Really old question but:
To export all the pages use excel.allPages:
$("#grid").kendoGrid({
toolbar: ["excel"],
excel: {
allPages: true
},
// ....
});
See Example
Grid toolbar
..
.ToolBar(toolbar =>
{
toolbar.Template(
#<text>
#Html.Kendo().Button().Name("grid-export").HtmlAttributes(new { type = "button", data_url = #Url.Action("Export") }).Content("Export").Events(ev => ev.Click("exportGrid"))
</text>);
})
..
Endpoint export function
public FileResult Export([DataSourceRequest]DataSourceRequest request)
{
DemoEntities db = new DemoEntities();
byte[] bytes = WriteExcel(db.Table.ToDataSourceResult(request).Data, new string[] { "Id", "Name" });
return File(bytes,
"application/vnd.ms-excel",
"GridExcelExport.xls");
}
a javascript function to generate grid remote export url with all specified parameters
function exportGrid() {
var toolbar = $(this.element);
var gridSelector = toolbar.closest(".k-grid");
var grid = $(gridSelector).data("kendoGrid");
var url = toolbar.data("url");
var requestObject = (new kendo.data.transports["aspnetmvc-server"]({ prefix: "" }))
.options.parameterMap({
page: grid.dataSource.page(),
sort: grid.dataSource.sort(),
filter: grid.dataSource.filter()
});
url = url + "?" + $.param({
"page": requestObject.page || '~',
"sort": requestObject.sort || '~',
"pageSize": grid.dataSource.pageSize(),
"filter": requestObject.filter || '~',
});
window.open(url, '_blank');
}
For detailed solution see my sample project on Github
where u can export grid server side with current configuration (sorting, filtering, paging) using helper function
I am developing a website where I use a custom build jQuery widget to load data into multiple divs.
This is the code for the widget:
(function ($, window, document, undefined) {
$.widget ("my.contentloader", {
options: {
loading_message: true
},
_create: function () {
var that = this;
$.ajax ({
type: "POST",
url: that.options.url,
data: {data: that.options.formdata, limit: that.options.limit, offset: that.options.offset},
beforeSend: function (html) {
if (that.options.loading_message) {
$(that.options.target_area).html ("<div id='loading'>Loading</div>");
}
},
success: function (html) {
if (that.options.loading_message) {
$('#loading').remove ();
}
$(that.options.target_area).html (html);
},
error: function (html) {
$(that.options.error_area).html (html);
}
});
},
_setOption: function (key, value) {
this.options[key] = value;
$.Widget.prototype._setOption.apply (this, arguments);
}
});
})(jQuery, window, document);
I load data using the widget like this:
$('#targetdiv').contentloader ({
url: '<?php echo $action_url; ?>',
target_area: '#popup_box',
formdata: {'username' : username_email, 'password' : password}
});
I am having problems loading multiple instances on the same page.
Is there a way to not instantiate the widget on a specific div like this?
$('#targetdiv').contentloader
I think you need to assign each instance to a variable. That way, you can control each instance, or write a function that iterates over an array of instances.
var contentLoaders = [];
$('.target-div').each(function(i, data) {
contentLoaders[i] = $.widget("my.contentloader", { ... });
});
So then you should be able to operate on each loader independently, like:
for (var i in contentLoaders) {
var contentLoader = contentLoaders[i];
contentLoader.option( ... );
}
Also, you're using the DOM ID $('#loading') for multiple instances of the widget. This is wrong. You need to either use separate loaders for each widget, or else check to see if the ID exists and only insert the new node if it doesn't exist. And same for removing it.
** I've added this example block, hope it helps: **
//
// This is a way to do it if you want to explicitly define each contentloader.
// Below that, I'll write out a way to define the contentloaders in a loop.
//
var contentLoader1 = $('#targetdiv1').contentloader ({
url: '<?php echo $action_url; ?>',
target_area: '#popup_box',
formdata: {'username' : username_email, 'password' : password}
});
contentLoader1.option('url', 'http://google.com');
var contentLoader2 = $('#targetdiv2').contentloader ({
url: '<?php echo $action_url; ?>',
target_area: '#popup_box',
formdata: {'username' : username_email, 'password' : password}
});
contentLoader2.option('url', 'http:/apple.com');
// Push each widget instance into an array of widget objects
var contentLoaders = [];
contentLoaders.push(contentLoader1);
contentLoaders.push(contentLoader2);
for (var i in contentLoaders) {
console.log(i, contentLoaders[i].option('url'));
}
// Should print:
// 0 http://google.com
// 1 http://apple.com
//
//
// How to set a bunch of widgets at once from an array of content loader data
//
//
var contentLoaderData = [
{
divid: '#targetDiv1',
url: 'google.com',
formdata: {
username: 'joeshmo',
password: 'joeshmo1'
}
},
{
divid: '#targetDiv2',
url: 'apple.com',
formdata: {
username: 'plainjane',
password: 'plainjane1'
}
}
];
// Array of widget instances
var contentLoaders = [];
$.each(contentLoaderData, function(index, value) {
var contentLoader = $(this.divid).contentloader({
url: this.url,
target_area: '#popup_box',
formdata: {'username' : this.formdata.username, 'password' : this.formdata.password}
});
// Push each contentLoader instance into the contentLoaders array
contentLoaders.push(contentLoader);
});
for (var i in contentLoaders) {
console.log(i, contentLoaders[i].option('url'));
}
// Should print:
// 0 http://google.com
// 1 http://apple.com