How to use multiple fineuploader instances with manual upload buttons with one template - fine-uploader

With the fine-uploader plugin I am trying to add multiple (dynamic could be 1, or 10) instances with an optional caption field and a manual upload button per section.
The form I am uploading from is dynamically generated in layout as well as content, the uploaded files have to be stored by the handler based upon the section of the form as well as the instance of fine-uploader. I also need the ability to effectively upload each instance of fine-uploader independently
The issue that I am hitting is following the guidelines & demo for the manual upload option, ie adding a click function it will always find only the first instance as it searches for the button using .getElementById.
I can get around this by defining a new template for each instance however I would prefer to use a single template.
The template code (for each instance - abbreviated for simplicity) is
<script type="text/template" id="qq-template-manual-trigger#XX#">
<div class="qq-uploader-selector qq-uploader" qq-drop-area-text="Drop files here">
...
<div class="buttons">
<div class="qq-upload-button-selector qq-upload-button">
<div>Select files</div>
</div>
<button type="button" id="trigger-upload#XX#" class="btn btn-primary">
<i class="icon-upload icon-white"></i> Upload
</button>
</div>
...
<ul class="qq-upload-list-selector qq-upload-list" aria-live="polite" aria-relevant="additions removals">
<li>
...
<input class="caption" tabindex="1" type="text">
...
</li>
</ul>
...
</div>
</script>
<div id="fine-uploader-manual-trigger#XX#"></div>
and the uploader script
<script>
var manualUploader#XX# = new qq.FineUploader({
element: document.getElementById('fine-uploader-manual-trigger#XX#'),
template: 'qq-template-manual-trigger#XX#',
request: {
inputName: "imagegroup[]",
endpoint: '/SaveFile.aspx'
},
autoUpload: false,
debug: true,
callbacks: {
onError: function(id, name, errorReason, xhrOrXdr) {
alert(qq.format("Error on file number {} - {}. Reason: {}", id, name, errorReason));
},
onUpload: function (id) {
var fileContainer = this.getItemByFileId(id)
var captionInput = fileContainer.querySelector('.caption')
var captionText = captionInput.value
this.setParams({
"descr[]": captionText,
<-- Other parameters here -->
}, id)
}
},
});
qq(document.getElementById("trigger-upload#XX#")).attach("click", function () {
manualUploader#XX#.uploadStoredFiles();
});
</script>
in the ideal world I would prefer simply have a single
<script type="text/template" id="qq-template-manual-trigger">
....
</script>
then where required multiple times through the form
<div id="fine-uploader-manual-trigger"></div>
<script>
var manualUploader#XX# = new qq.FineUploader({
element: document.getElementById('fine-uploader-manual-trigger'),
template: 'qq-template-manual-trigger',
...
}
qq(document.getElementById("trigger-upload")).attach("click", function () {
manualUploader#XX#.uploadStoredFiles();
});
</script>
The use of the attach function by calling .getElementById just feels wrong, or at the very least cludgy, is there a better way of activating the upload on a per-instance basis?
Thanks in advance
K

Sorted, but if anyone has a better answer...
Instead of using the demo of document.getElementById("trigger-upload")
Simply use document.querySelector("#fine-uploader-manual-trigger #trigger-upload")
eg
<div id="fine-uploader-manual-triggerXX"></div>
<script>
var manualUploaderXX = new qq.FineUploader({
element: document.getElementById('fine-uploader-manual-triggerXX'),
template: 'qq-template-manual-trigger',
... // omitted for brevity
}
qq(document.querySelector("#fine-uploader-manual-triggerXX #trigger-upload")).attach("click", function () {
manualUploaderXX.uploadStoredFiles();
});
</script>

Related

Passing the Div id to another vue component in laravel

I created a simple real-time chat application using vue js in laravel.
I am having a problem with the automatic scroll of the div when there is a new data.
What I want is the div to automatically scroll down to the bottom of the div when there is a new data.
Here is my code so far.
Chat.vue file
<template>
<div class="panel-block">
<div class="chat" v-if="chats.length != 0" style="height: 400px;" id="myDiv">
<div v-for="chat in chats" style="overflow: auto;" >
<div class="chat-right" v-if="chat.user_id == userid">
{{ chat.chat }}
</div>
<div class="chat-left" v-else>
{{ chat.chat}}
</div>
</div>
</div>
<div v-else class="no-message">
<br><br><br><br><br>
There are no messages
</div>
<chat-composer v-bind:userid="userid" v-bind:chats="chats" v-bind:adminid="adminid"></chat-composer>
</div>
</template>
<script>
export default {
props: ['chats','userid','adminid'],
}
</script>
ChatComposer.vue file
<template>
<div class="panel-block field">
<div class="input-group">
<input type="text" class="form-control" v-on:keyup.enter="sendChat" v-model="chat">
<span class="input-group-btn">
<button class="btn btn-primary" type="button" v-on:click="sendChat">Send Chat</button>
</span>
</div>
</div>
</template>
<script>
export default{
props: ['chats','userid','adminid'],
data() {
return{
chat: ''
}
},
methods: {
sendChat: function(e) {
if(this.chat != ''){
var data = {
chat: this.chat,
admin_id: this.adminid,
user_id: this.userid
}
this.chat = '';
axios.post('/chat/sendChat', data).then((response) => {
this.chats.push(data)
})
this.scrollToEnd();
}
},
scrollToEnd: function() {
var container = this.$el.querySelector("#myDiv");
container.scrollTop = container.scrollHeight;
}
}
}
</script>
I am passing a div id from the Chat.vue file to the ChatComposer.vue file.
As you can see in the ChatComposer.vue file there is a function called scrollToEnd where in it gets the height of the div id from Chat.vue file.
When the sendchat function is triggered i called the scrollToEnd function.
I guess hes not getting the value from the div id because I am getting an error - Cannot read property 'scrollHeight' of null.
Any help would be appreciated.
Thanks in advance.
the scope of this.$el.querySelector will be limited to only ChatComposer.vue hence child component can not able to access div of parent component #myDiv .
You can trigger event as below in ChatComposer
this.$emit('scroll');
In parent component write ScollToEnd method and use $ref to assign new height
<chat-composer v-bind:userid="userid" v-bind:chats="chats" v-bind:adminid="adminid" #scroll="ScollToEnd"></chat-composer>
..

Updating a polymer element property with data from API call

I'm trying to update a property in a polymer element with data from an ajax api call. I have something similar working elsewhere in the app where users are able to add packages dynamically.
Anyone know what I'm doing wrong here?
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="address-input.html">
<link rel="import" href="package-list.html">
<link rel="import" href="../bower_components/iron-ajax/iron-ajax.html">
<dom-module id="step-one">
<style>
</style>
<template>
<section id="addresses">
<div class="container">
<div class="row">
<h5>Addresses</h5>
<address-input></address-input>
</div>
</div>
</section>
<section id="packages">
<div class="container">
<div class="row">
<h5>Packages</h5>
<package-list></package-list>
</div>
</div>
</section>
<section id="submit-shipping-info">
<div class="container">
<div class="row">
<a class="waves-effect waves-light btn col s12 m12 l12" id="submit" on-click="submitInfo">Submit</a>
<template is="dom-repeat" items="{{options}}">
<p>{{item.rates}}</p>
</template>
</div>
</div>
</section>
</template>
</dom-module>
<script>
Polymer ({
is: 'step-one',
properties: {
options: {
type: Object,
notify: true,
value: []
}
},
submitInfo: function(e) {
e.preventDefault();
//add dimensions of all packages to the dimensions array
var dimensions=[];
$('#packages .package-card').each(function(){
var weight= $(this).find('.weight').val();
var length= $(this).find('.length').val();
var height= $(this).find('.height').val();
var width= $(this).find('.width').val();
var dimension={width:width,length:length,height:height,weight:weight};
dimensions.push(dimension);
});
//capture address data
var from = $('#fromAddress').val();
var to = $('#toAddress').val();
//URL that processes getting a URL
var getQuoteURL = '../v2/API/get_rates.php';
var stuff = [];
jQuery.ajax({
type: "POST",
dataType: "json",
cache: false,
url: getQuoteURL,
data:{
from:from,
to:to,
dimension:dimensions
}
}).done(function(data){
$.each(data['rates'], function(i, rate ) {
stuff.push({carrier:rate.carrier});
return stuff;
});
//show step two when ajax call completes
$('.step-two').removeClass('hide').addClass('show');
console.log(stuff);//I can see all objects I need to pass to the 'options' property
return stuff;
});
this.push('options',stuff);//doesn't seem to update the 'options' property with these as a value
}
});
</script>
I'm able to console.log the array i'm trying to use, but when I try to push it to the 'options' property, it won't update.
Consider using Polymer built in methods instead of jQuery.
1. A button to submit a request.
<paper-button on-click="handleClick">Send a package</paper-button>
2. AJAX requests using <iron-ajax> element!
<iron-ajax id="SendPkg"
url="my/api/url"
method="POST"
headers='{"Content-Type": "application/json"}'
body={{packageDetails}}
on-response="handleResponse">
</iron-ajax>
3. Handle the on-click event,
On click, select <iron-ajax> by ID and call <iron-ajax>'s generateRequest()
Use either data binding or Polymer's DOM API to get the package's width, height ...etc
handleClick: function() {
this.packageDetails = {"width": this.pkgWidth, "height": this.pkgHeight };
this.$.SendPkg.generateRequest();
},
4. Handle the response
handleResponse: function() {
//Push data to options...
},
return stuff;
});
this.push('options',stuff);//doesn't seem to update the 'options' property with these as a value
should be
return stuff;
this.push('options',stuff);//doesn't seem to update the 'options' property with these as a value
)};
otherwise
this.push('options',stuff);
is executed before data has arrived
The solution ended up being to put this into a variable:
var self = this;
then in the ajax .done() replace the value of the object with the new object from the ajax call.
self.options = stuff;
I guess you have to put "this" into a variable before you can overwrite it's values. Then the other issue was that I was trying to use .push() to add to it, but really all I needed to do was replace it. (Using self.push('options',stuff); didn't seem to work as far as adding to an object)

Display data in a Vue component with ajax

I seem to be misunderstanding how to pass data to a Vue.js component with an ajax call.
My understanding of how this should work:
I need to create an empty object called campaigns in the data section of my component.
Then call method "fetchCampaigns" on page ready to replace the data object.
fetchCampaign method completes an AJAX call and inside of the success callback use this.$set('campaigns', campaigns) to replace the empty campaign object with the newly returned campaign object
Use v-for on the template to iterate through the campaign object and access values with #{{campaign.type}}
My html (I am use vue router, vue resource and laravel blade) :
<router-view></router-view>
<template id="campaignBlock" v-for="campaign in campaigns">
<div class="row">
<div class="block">
<div class="block-title">
<h2>Type: <em>#{{campaign.id}}</em></h2>
</div>
<div class="row"><!-- Grid Content -->
<div class="hidden-sm hidden-xs col-md-4 col-lg-4">
<h2 class="sub-header">#{{campaign.title}}</h2>
</div>
</div>
</div><!-- END Grid Content -->
</template>
Vue component
Vue.component('app-page', {
template: '#campaignBlock',
data: function() {
return{
campaigns: []
}
},
ready: function () {
this.fetchCampaigns();
},
methods: {
fetchCampaigns: function () {
var campaigns = [];
this.$http.get('/retention/getCampaigns')
.success(function (campaigns) {
this.$set('campaigns', campaigns);
})
.error(function (err) {
campaigns.log(err);
});
},
}
})
This is the result of my ajax call from console:
{"campaigns":[{"id":1,"user_id":2,"target_id":1,"name":"Test Campaign","description":"This is a test Campaign","target":"Onboarding","created_at":"-0001-11-30 00:00:00","updated_at":"-0001-11-30 00:00:00","deleted_at":null}]}
I'm not sure why I can't get my vue component to recognize the new data. Anyone see what I'm missing? TIA
Turns out that v-for="campaign in campaigns" should not go on the template tag, but inside of it.
So this:
<template id="campaignBlock" v-for="campaign in campaigns">
<div class="row">
Should be changed to this:
<template id="campaignBlock">
<div class="row" v-for="campaign in campaigns">

Prevent component from rendering input attributes

I have a my-tag component that simply renders a title:
html
<div id="content"></div>
<script id="main-template" type="text/mustache">
<my-tag title="This is the title"></my-tag>
</script>
javascript
var Component = can.Component.extend({
tag: 'my-tag',
template: '<h1>{{title}}</h1>',
viewModel: {
title: '#'
}
});
$('#content').html(can.view('main-template', {}));
output
<div id="content">
<my-tag title="This is the title">
<h1>This is the title</h1>
</my-tag>
</div>
I would like to have the output as follows:
<div id="content">
<my-tag>
<h1>This is the title</h1>
</my-tag>
</div>
How can I get the component to not render the title attribute in my-tag?
Here is the jsfiddle.
You can't prevent it from rendering, however, you might be able to remove it after the component is created like:
var Component = can.Component.extend({
tag: 'my-tag',
template: '<h1>{{title}}</h1>',
viewModel: {
title: '#'
},
events: {
init: function(){
this.element.removeAttr("title");
}
}
});
Also, if you are starting a new CanJS project, I'd encourage you to switch to can.stache as that will be the default templating engine in 3.0. It's highly compatible with can.mustache.

What's the best way to add the same ajax function to a list of comments?

Source code is like this:
<div>
<h4>comment content</h4>
<a id="delcmt_{{ comment.id }}">delete this comment</a>
</div>
......
<div>
<h4>comment content</h4>
<a id="delcmt_{{ comment.id }}">delete this comment</a>
</div>
I what to add ajax function to each of the "delete this comment" link:
<script type=text/javascript>
$(function() {
$('a#delcmt_id').bind('click', function() {
$.get($SCRIPT_ROOT + '/del_comment', {
}, function(data) {
$("#result").value(data.result);
});
return false;
});
});
</script>
What I can come out is using a loop to copy the upper ajax function for each comment, that must be very ugly. Any good ideas?
Try adding a class and select it with jquery add an event handler. You have to use the 'on' event because the elements you wish attach behavior to might be dynamic and load after document ready.
#*Render all this with razor, or angular or knockout*#
<div>
<h4>comment content</h4>
<span style="cursor: pointer;" id="1" data-rowid="1" class="delete-me-class">delete this comment</span>
</div>
<div>
<h4>comment content</h4>
<span style="cursor: pointer;" id="2" data-rowid="2" class="delete-me-class">delete this comment</span>
</div>
<script>
$(function () {
$('body').on('click', '.delete-me-class', function () {//http://api.jquery.com/on/ on is the latest 'live' binding for elements that may not exists when DOM is ready.
var rowId = $(this).data('rowid');
//TODO Use rowId for your delete ajax, or your element Id if you wish.
alert('You clicked on the delete link with the row ID of ' + rowId);
});
});
</script>
Here is a working Fiddle

Resources