Multiple validations in FineUploader - reactjs

I'm using FineUploader in my React app to upload files to Azure Blob Storage. I'm currently using validation to make sure a user can only upload one file. I now want to add two more validations:
I want to allow only JPG and PNG files
I also want to make sure that the pixel size of the file user can upload must be at least 300x300 pixels. In other words, I want to impose both width and height requirements
How do I add these validations? Do I need a validation property inside options for each requirement or do they go into the existing one? My current validation looks like this:
// Omitted for brevity
constructor(props) {
super(props);
this.uploader = new FineUploaderAzure({
options: {
cors: {
expected: true,
sendCredentials: false
},
signature: {
endpoint: "some-url.com"
},
request: {
endpoint: "my-container-url"
},
validation: {
multiple: false
}
},
callbacks: {
onError: function (id, name, errorReason, xhrOrXdr) {
}
}
})
}

Assume react/fine-uploader don't change it, by Documentation, you should do it like this.
options: {
multiple: false,
cors: {
expected: true,
sendCredentials: false
},
signature: {
endpoint: "some-url.com"
},
request: {
endpoint: "my-container-url"
},
validation: {
allowedExtensions: ['jpeg', 'jpg', 'png'],
image:{
minHeight: 300,
minWidth: 300
}
}
}
PS. I didn't use fineuploader in react, so please see if this is working as well in react.
Answer to update
In case you want to have your own validation, first of all you should not use onError.
onError is invoked if and only if error occurs. You validate the image size, though the image doesn't match your criteria, it is NOT error.
Then you have two choice to do your validation, that depends on what effect you want. First you need to know the flow of events. It is:
onSubmit -> validation -> onValidate
The work (check is image or not) you most likely want can be done on both three places.
If you want it in the validation, then the answer I provided already made it (allowedExtensions: ['jpeg', 'jpg', 'png']). But let's say you want to make some customization of alert or other checking, you need to do it in onValidate.
callbacks:{
onValidate: function(data, button_container) {
console.log(data); //data.name && data.size
return false;
}
}
But please be reminded that in onValidate, you can only get back file name and file size, no other information.
If you want to also check the width & height, you probably need to take a look on this SO question (give OP a upvote if you find this is what you want.)

Related

How to add 3rd party editor to Wagtail 2.2+?

We have been using Froala editor within Wagtail for years, and even though Draftail is nice, the end user wants to continue to use Frola, especially as a license fee has been paid for it, and it offers extra functionality that they use.
I have used the following which works great for any version of Wagtail prior to version 2.2:
https://github.com/jaydensmith/wagtailfroala/pull/5/commits/d64d00831375489cfacefa7af697a9e76fb7f175
However in Wagtail version 2.2 this has changed:
https://docs.wagtail.org/en/stable/releases/2.2.html#javascript-templates-in-modal-workflows-are-deprecated, this causes selecting images within Froala to no longer work correctly. You get to pick the image, but it fails to move onto the 2nd dialog to select alignment and then click insert.
It looks like wagtailfroala/static/froala/js/froala.js needs to be changed to add onload to the ModalWorkflow.
$.FE.RegisterCommand('insertImage', {
title: 'Insert Image',
undo: false,
focus: true,
refreshAfterCallback: false,
popup: true,
callback: function () {
var editor = this;
return ModalWorkflow({
url: window.chooserUrls.imageChooser + '?select_format=true',
responses: {
imageChosen: function(imageData) {
editor.edit.off();
var $img = $(imageData.html);
$img.on('load', function() {
_loadedCallback(editor, $(this));
});
// Make sure we have focus.
// Call the event.
editor.edit.on();
editor.events.focus(true);
editor.selection.restore();
editor.undo.saveStep();
// Insert marker and then replace it with the image.
if (editor.opts.imageSplitHTML) {
editor.markers.split();
} else {
editor.markers.insert();
}
var $marker = editor.$el.find('.fr-marker');
$marker.replaceWith($img);
editor.html.wrap();
editor.selection.clear();
editor.events.trigger('contentChanged');
editor.undo.saveStep();
editor.events.trigger('image.inserted', [$img]);
}
}
});
},
plugin: 'image'
});
But what needs to be added to make it work? I can't find any documentation or examples on how do to this? Please help or point me in the direction of the documentation for how this should be done.
Thank you so much in advance.

Ng-admin: How to show different fields according to user's selection?

I am using ng-admin to write a admin management. I met below issue, can somebody help me?
In my creationView, I would like to show different fields(text/video/pictures) according to the selection of "type" field. How I can make it?
var articles = nga.entity('articles');
articles.creationView().fields([
nga.field('type','choice')
.validation({ required: true })
.choices([
// 1: txt, 2: pic, 3: vid
{ value: 1, label: 'Text'},
{ value: 2, label: 'Picture'},
{ value: 3, label: 'Video'},
]),
nga.field('txt','file')
.attributes({ placeholder: 'Write something... !' }),
nga.field('pic','file')
.label('Picture(.jpg|.png)')
.uploadInformation({ 'url': '/api/adminapi/uploadPicture', 'apifilename': 'pictures', 'accept': 'image/jpg,image/png'}),
nga.field('vid','file')
.label('Video(.mp4)')
.uploadInformation({ 'url': '/api/adminapi/uploadVideo', 'apifilename': 'video', 'accept': 'video/mp4'}),
])
The Field Configuration doc page explains how to do this using the "template" field config:
template(String|Function, templateIncludesLabel=false) All field types
support the template() method, which makes it easy to customize the
look and feel of a particular field, without sacrificing the native
features.
...
To force the template to replace the
entire line (including the label), pass true as second argument to the
template() call. This can be very useful to conditionally hide a field
according to a property of the entry:
post.editionView()
.fields([
nga.field('category', 'choice')
.choices([
{ label: 'Tech', value: 'tech' },
{ label: 'Lifestyle', value: 'lifestyle' }
]),
nga.field('subcategory', 'choice')
.choices(function(entry) {
return subCategories.filter(function (c) {
return c.category === entry.values.category;
});
})
// display subcategory only if there is a category
.template('<ma-field ng-if="entry.values.category" field="::field" value="entry.values[field.name()]" entry="entry"
entity="::entity" form="formController.form"
datastore="::formController.dataStore"></ma-field>', true),
]);
I just have a work round method. That is using .attributes(onclick, "return updateButton();") for nga.field("type"). In updateButton() method, it will help to check current 'type' field value and using DOM methods to change the button enable and disabled.
However, I still realy appreciate that if this requirement can be support in future. It will be helpful for user to make UI controls easily.

Extending UI-Router-Tabs To Display Form Validation State

I'm pretty new to Angular and having a problem figuring out how to bind a value from a data service to a customization of the https://github.com/rpocklin/ui-router-tabs project that I have created.
The customization is to display an icon in the tab heading that toggles based on the validation state of the corresponding ui-view. I've wired up a data service that is updated when validation succeeds or fails, and the binding is being "seen" by the controller that renders the tabs.
The problem arises when I attempt to pass this value into the tab data as "options". The value change/icon toggle is not being processed as the validation state changes. If I pass a hard-coded "true" into the tab data, the desired icon is displayed.
vm.tabs = [
{
heading: "View 1",
route: "manage.view1",
options: {
status: vm.stateService.getForm1State()
}
},
{
heading: "View 2",
disable: vm.disableTabs,
route: "manage.view2",
options:{
status: true
}
}
];
This doesn't seem like something that should be that difficult, so I think I'm just missing something obvious about the scoping. Here is a plunk http://plnkr.co/edit/iefvwcffSZmpfy83NGde?p=preview that demonstrates the issue.
Note: should be tagged as ui-router-tabs, but I lack the reputation to create the tag.
The problem lies here:
options: {
status: vm.stateService.getForm1State()
}
Since stateService.getForm1State() returns a boolean value, this value will be copied and assigned as the value of the status property.
So when this code has executed once it will for example be:
options: {
status: false
}
The options object won't react to any changes within the stateService.
An easy solution is to have stateService.getForm1State() return an object instead.
For example:
function stateService() {
var form1IsValid = {
status: false
};
return {
getForm1State: getForm1State,
setForm1State: setForm1State
};
function getForm1State() {
return form1IsValid;
}
function setForm1State(newValue) {
form1IsValid.status = newValue;
return;
}
Then make the options object refer to the same object:
options: vm.stateService.getForm1State()
Now whenever you have:
options.status
It will be the same as:
vm.stateService.getForm1State().status
And it will correctly reflect the changes.
Demo: http://plnkr.co/edit/KjXVSSMJbLw7uod0ac6O?p=preview

How to post many-to-many relational data to a restful api from AngularJS?

I have a Theme Management module in my web application. I'รถ using SequelizeJS in server side.
Models are:
module.exports = function(sequelize, DataTypes) {
var Theme = sequelize.define('Theme', {
name: DataTypes.STRING,
description: DataTypes.STRING
}, {
associate: function(models) {
Theme.belongsToMany(models.Option, { through: models.ThemeOptions })
},
tableName: 'themes'
});
return Theme;
};
module.exports = function(sequelize, DataTypes) {
var ThemeOptions = sequelize.define('ThemeOptions', {
}, {
tableName: 'theme_options'
});
return ThemeOptions;
};
module.exports = function(sequelize, DataTypes) {
var Option = sequelize.define('Option', {
key: DataTypes.STRING,
value: DataTypes.STRING
}, {
associate: function(models) {
Option.belongsToMany(models.Theme, { through: models.ThemeOptions })
},
tableName: 'options',
timestamps: false
});
return Option;
};
In /#/themes/create state, I want to create a theme with some options, like color codes.
I am creating a theme with
$http.post('/themes', themeData)
then with it's it, I am creating options. Finally I should post many-to-many data to theme options. So for a theme that has 10 options, I am posting 21 times.
What is the best way to post many-to-many data to a REST server?
Don't really know how you handle your routing or backend. Supposing you're using express and your options are previously created, I'd recommend creating a new post route for handling each ThemeOptions
app.post('/themeOptions', { ThemeId: 1, OptionId:2 });
and use that info to create a ThemeOptions instance to join a Theme with certain Option.
This could reduce your post quantity to the half + 1 (one for the Theme and one for each ThemeOption).
Another solution is to maybe manage an array of ThemeOptions and use ThemeOptions#bulkCreate to create them at once, using only 2 posts (one for the Theme and one for all the ThemeOptions.
Would be something like this:
app.post('/themeOptions', {
options: [{
ThemeId: 1,
OptionId:2
}, {
ThemeId: 1,
OptionId:3
}
// and so on...
});
Each of these solutions could involve more logic to manage each front end request, but could increase front end behavior as well.
A final (and more complex at the backend) solution would be to send a unique post sending both, the Theme and the Options array, and create all the ThemeOptions after creating the Theme
// frontend
app.post('/theme', {
theme: {
name: 'John',
description: 'Doe'
},
options: [2, 3 /* and so on ... */]
});
// backend
Theme.create(req.body.theme).on('success', function (theme) {
var options = req.body.options.map(function (option) {
return {
ThemeId: theme.id,
OptionId: option
};
});
ThemeOptions.bulkCreate(options);
})
In the Symfony2 world, there is a bundle, SonataAdminBundle, that generates admin interfaces. With the entity classes (here, Option and Theme), it generates all the pages, listing, creating and editing theses entities. It generates forms that handles many to many relationship. Here how it manages that :
The user consults the creation/edition form of the entity of any side of the ManyToMany relation. In the form, where it have to display the many to many association, it displays a <select>, with Select2 for instance, which is a <select> with some jQuery. Each element of the list is linked with the corresponding ID in the database, something like <option value="13456">Option #3</select>. For a many to many relationship, we can select multiple fields at the time. Internally, it builds an array of Option IDs with the <select>.
If we want to add a inverse entity on the fly (here, the inverse entity is Option, I think ...), there is a button that open ups an Option creation form, and once the new Option is added, it adds the newly created option in the <select>, so the user can add it in the form immediately.
Then, it sends the array of Option ID's built with the form.
I think this strategy could fit your needs.

Downloading image/text file using 'iframe'

I am using Extjs - 4.1.0 and I want to prompt download window. I used 'iframe'. The window displays only when the file being posted is zip/mp3 but when the file being posted is text/image file, the window doesn't appear. Is there some property to be set to enable download window for text/image/different files?
Please find my code below.
var record = item.findParentByType('itemcontextmenu').record;
Ext.Ajax.request({
url : ORT.Configuration.DOWNLOAD_GRAPHICS_URI+"&graphics="+record.get('id'),
success: function (response, opt) {
result = Ext.decode(response.responseText);
try {Ext.destroy(Ext.get('graphicsDownloadIframe'));}catch(e) {}
Ext.core.DomHelper.append(document.body, {
tag: 'iframe',
id:'graphicsDownloadIframe',
css: 'display:none;visibility:hidden;height:0px;',
src: result.fileName,
frameBorder: 0,
width: 0,
height: 0
});
}
});
It's all about the mimetype and what your browser does with it. So I guess your problem will the that. Which likely will cause the browser to handle the received file different.
Edit
Following is a helper class that I still use
Ext.ux.util.HiddenForm = function(url,fields){
if (!Ext.isArray(fields))
return;
var body = Ext.getBody(),
frame = body.createChild({
tag:'iframe',
cls:'x-hidden',
id:'hiddenform-iframe',
name:'iframe'
}),
form = body.createChild({
tag:'form',
cls:'x-hidden',
id:'hiddenform-form',
action: url,
target:'iframe'
});
Ext.each(fields, function(el,i){
if (!Ext.isArray(el))
return false;
form.createChild({
tag:'input',
type:'text',
cls:'x-hidden',
id: 'hiddenform-' + el[0],
name: el[0],
value: el[1]
});
});
form.dom.submit();
return frame;
}
Usage
Ext.ux.util.HiddenForm('http://localhost/file/fetch',[['PropName','PropValue'],['Prop2Name','Prop2Value']])
The server take these arguments and build up a sort of 'FileStreamResult' which trigger a download on the clientside. If you are using a language like .Net or Java then there are already implementations you can use, if you are using PHP you might find this helpful. Anyway, look at your backend...

Resources