I have a requirement to show time estimates for each file currently in progress of being uploaded. We are testing out the UI portion of the uploader and wondered if there is a way to add this in near the current % complete/size information that is currently displayed.
I don't really want to make my own entire custom UI just for that purpose.
You can add custom elements to Fine Uploader's template, and these elements will be rendered onto the DOM which you can than manipulate via JavaScript.
<script type="text/template" id="qq-template">
<div class="qq-uploader-selector qq-uploader">
...
<ul class="qq-upload-list-selector qq-upload-list">
<li>
...
<!-- custom element for updating with progress -->
<span class="file-progress"></span>
</li>
</ul>
...
</div>
</script>
You would then have to tie into Fine Uploader's events to update that element when the file is submitted (onSubmitted) or progresses (onProgress). To do this you'd use the getItemByFileId API method to select the file list item in the DOM, and then use JS to select that child element containing the progress element that you wanted to update, and -- naturally -- update it as you would.
// ...
onSubmitted: function (id, name) {
var el = getElementByFileId(id) // retrieves the list element for this file id.
// initialze the progress element, with 0% for example
},
// ...
onProgress: function (id, name, uploadedBytes, totalBytes) {
// update as the file progresses ....
}
// ...
The documentation has more examples on how easy it is to add custom elements to the DOM.
Related
I'm new to Alpine and struggling to wrap my head around how to make a scenario like this work:
Let's say I have a serverside built page, that contains some buttons, that represent newsletters, the user can sign up to.
The user might have signed up to some, and we need to indicate that as well, by adding a css-class, .i.e is-signed-up.
The initial serverside markup could be something like this:
<button id='newsletter-1' class='newsletter-signup'>Newsletter 1</button>
<div>some content here...</div>
<button id='newsletter-2' class='newsletter-signup'>Newsletter 2</button>
<div>more content here...</div>
<button id='newsletter-3' class='newsletter-signup'>Newsletter 3</button>
<div>and here...</div>
<button id='newsletter-4' class='newsletter-signup'>Newsletter 4</button>
(When all has loaded, the <button>'s should later allow the user to subscribe or unsubscribe to a newsletter directly, by clicking on one of the buttons, which should toggle the is-signed-up css-class accordingly.)
Anyway, then I fetch some json from an endpoint, that could look like this:
{"newsletters":[
{"newsletter":"newsletter-1"},
{"newsletter":"newsletter-2"},
{"newsletter":"newsletter-4"}
]}
I guess it could look something like this also:
{"newsletters":["newsletter-1", "newsletter-2", "newsletter-4"]}
Or some other structure, but the situation would be, that the user have signed up to newsletter 1, 2 and 4, but not newsletter 3, and we don't know that, until we get the JSON from the endpoint.
(But maybe the first variation is easier to map to a model, I guess...)
Anyway, I would like to do three things:
Make Alpine get the relation between the model and the dom elements with the specific newsletter id (i.e. 'newsletter-2') - even if that exact id doesn't exist in the model.
If the user has signed up to a newsletter, add the is-signed-up css-class to the corresponding <button> to show its status to the user.
Bind to each newsletter-button, so all of them – not just the ones, the user has signed up to – listens for a 'click' and update the model accordingly.
I have a notion, that I might need to 'prepare' each newsletter-button beforehand with some Alpine-attributes, like 'x-model='newsletter-2', but I'm still unsure how to bind them together when Alpine has initialising, and I have the data from the endpoint,
How do I go about something like this?
Many thanks in advance! 😊
So our basic task here is to add/remove a specific item to/from a list on a button click. Here I defined two component: the newsletter component using Alpine.data() creates the data (subs array), provides the toggling method (toggle_subscription(which)) and the checking method (is_subscribed(which)) that we can use to set the correct CSS class to a button. It also handles the data fetching in the init() method that executes automatically after the component is initialized. I have also created a save method that we can use to send the subscription list back to the backend.
The second component, subButton with Alpine.bind() is just to make the HTML code more compact and readable. (We can put each attribute from this directly to the buttons.) So on click event it calls the toggle_subscription with the current newsletter's key as the argument to add/remove it. Additionally it binds the bg-red CSS class to the button if the current newsletter is in the list. For that we use the is_subscribed method defined in our main component.
.bg-red {
background-color: Tomato;
}
<script src="https://unpkg.com/alpinejs#3.x.x/dist/cdn.min.js" defer></script>
<div x-data="newsletter">
<button x-bind="subButton('newsletter-1')">Newsletter 1</button>
<button x-bind="subButton('newsletter-2')">Newsletter 2</button>
<button x-bind="subButton('newsletter-3')">Newsletter 3</button>
<button x-bind="subButton('newsletter-4')">Newsletter 4</button>
<div>
<button #click="save">Save</button>
</div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('newsletter', () => ({
subs: [],
init() {
// Fetch list of subscribed newsletters from backend
this.subs = ['newsletter-1', 'newsletter-2', 'newsletter-4']
},
toggle_subscription(which) {
if (this.subs.includes(which)) {
this.subs = this.subs.filter(item => item !== which)
}
else {
this.subs.push(which)
}
},
is_subscribed(which) {
return this.subs.includes(which)
},
save() {
// Send this.sub to the backend to save active state.
}
}))
Alpine.bind('subButton', (key) => ({
'#click'() {
this.toggle_subscription(key)
},
':class'() {
return this.is_subscribed(key) && 'bg-red'
}
}))
})
</script>
I have a kendo ui listview with a template the conditionally hides elements based on the underlying data. An example would be as follows:
<script type="text/x-kendo-template" id="template">
<div class="product">
<img src="../content/web/foods/#= ProductID #.jpg" alt="#: ProductName # image" />
<h3>#:ProductName#</h3>
<p>#:kendo.toString(UnitPrice, "c")#</p>
<div>
# if (Discontinued) { #
Discontinued Product
# } #
</div>
</div>
</script>
If i modify the underlying dataSource items to set Discontinued with the following code:
data[index].set('Discontinued', true);
If the index is the currently selected item then that item looses focus and is no longer selected.
Please see the following dojo example http://dojo.telerik.com/UlOze, select an item from the list and then set it to discontinued.
Has anybody found a solution / workaround for this issue ?
Thanks.
------------- FINAL SOLUTION --------------
Following on from dimodi's answer below I pieced together the solution.. For this to work the dataSource must have the schema -> model -> id property set.
1st capture the currently selected data item:
var selectedItem = $(listElement).find(".k-state-selected");
var selectedDataItem = list.dataItem(selectedItem);
2nd: After calling .set re-find the data item and set the k-state-selected class. This is nessesary as the list component is regenerating the uid's.
if (selectedDataItem) {
var newSelectedItem = list.dataSource.get(selectedDataItem.ProductID)
var uid = newSelectedItem.uid;
jQuery("[data-uid='" + uid + "']").addClass("k-state-selected");
}
I've updated the original dojo to show this solution, incase it helps someone else.
When a data item is changed, its corresponding ListView item is re-rendered to apply the changes. As a result, the selection is lost, as it is a purely visual feature that is not persisted across rebinds. You can check if an item is selected before using set() and then restore the selection manually by applying a k-state-selected class to the element afterwards.
I'm using codeigniter v3 and bootstrap v3.
I've a table for messages and for every message in table, when click on details button, a bootstrap modal show the details.
In a for loop, I print table rows and for every row (at end of the loop), I put whole bootstrap modal structure.
My question is: how could do this with ajax calling? I mean, I don't put all modal code for every table row (every message) and every time that details button clicked, I handle showing modal with ajax.
thanks for attention.
SOLVED:
I find an easy and I think better way in the bootstrap official website: Using data- attribute.
I show it with an working example for who that could not achieve better solution:
Suppose we want to fill a table body with some data (as my problem):
//just table body code
<tbody>
<?php foreach($fields as $field): ?>
<tr>
<td><?php $field->some_field; ?></td>
//other <td> elements
<td>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#myModal"
data-time="<?php echo $field->time; ?>">Show Details</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
So in a loop, I read data (that in controller, get with model's method, and pass to view with $data['fields']). Now for showing some details in modal, I use HTML5 data attribute. Suppose, I want to show time in the modal. As you see in above code, I put time field in data-time:
data-time="<?php echo $field->time; ?>"
Now I create a modal as template (you could see whole modal structure in bootstrap official website) and put it out of loop (one modal for all table rows; dynamic data). in the following code, I put an element in the modal body (as a placeholder):
//... in the modal body section
<h4 id="time"></h4>
This element have no content, because I want to retrieve every row time filed and put it in this element. Note this will be practical with defining an id. Now some script:
//first load jquery
<script type="text/javascript">
$(document).ready(function(){
$('#myModal').on('show.bs.modal', function(event){
var btn = $(event.relatedTarget); // find which button is clicked
var time = btn.data('time'); //get the time data attribute
$('#time').text(time); //put the data value in the element which set in the modal with an id
});
});
</script>
You could define more data attribute and retrieve in this way.
I will not write the code for you, but show you the way:
You just need one modal for the whole page and it works like a template.
You need same data/informations to be displayed in the modal. This data you can pick up via Javascript/jQuery from your HTML. I prefer data attributes for that. Something like:
data-modal-title="My Title" data-modal-content="My Content"
Now you inject this data in your modal template and open the modal via Javascript.
MVC/Razor/Javascript newbie question:
I have a MVC3/Razor form where the use can select a single product from a drop down list.
<div class="editor-label">
Product
</div>
<div class="editor-field">
#Html.DropDownList("ProductID", (IEnumerable<SelectListItem>)ViewBag.Products, "--Select One--")
#Html.ValidationMessageFor(model => model.ProductID)
</div>
What I then want is to display the price of the selected product on a label just below the drop down list (model property name is Amount).
This should be pretty easy, but I am pretty new at Razor, and know almost nothing about Javascript, so I would appreciate any verbose explanations of how do do it, and how it all hangs together.
Add a div/span under the Dropdown .
#Html.DropDownList("ProductID", (IEnumerable<SelectListItem>)ViewBag.Products, "--Select One--")
<div id="itemPrice"></div>
and in your Script, make an ajax call to one of your controller action where you return the price.
$(function(){
$("#ProductId").change(function(){
var val=$(this).val();
$("#itemPrice").load("#Url.Action("GetPrice","Product")", { itemId : val });
});
});
and have a controller action like this in your Product controller
public string GetPrice(int itemId)
{
decimal itemPrice=0.0M;
//using the Id, get the price of the product from your data layer and set that to itemPrice variable.
return itemPrice.ToString();
}
That is it ! Make sure you have jQuery loaded in your page and this will work fine.
EDIT : Include this line in your page to load jQuery library ( If it is not already loaded),
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
The Amount isn't available to your view when the user selects a product (remember the page is rendered on the server, but actually executes on the client; your model isn't available in the page on the client-side). So you would either have to render in a JavaScript array that contains a lookup of the amount based on the product which gets passed down to the client (so it's available via client-side JavaScript), or you would have to make a callback to the server to retrieve this information.
I would use jQuery to do this.
Here's a simple example of what the jQuery/Javascript code might look like if you used an array.
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>
$(document).ready(function() {
// This code can easily be built up server side as a string, then
// embedded here using #Html.Raw(Model.NameOfPropertyWithString)
var list = new Array();
list[0] = "";
list[1] = "$1.00";
list[2] = "$1.25";
$("#ProductID").change(displayAmount).keypress(displayAmount);
function displayAmount() {
var amount = list[($(this).prop('selectedIndex'))];
$("#amount").html(amount);
}
});
</script>
<select id="ProductID" name="ProductID">
<option value="" selected>-- Select --</option>
<option value="1">First</option>
<option value="2">Second</option>
</select>
<div id="amount"></div>
You'll want to spend some time looking at the docs for jQuery. You'll end up using it quite a bit. The code basically "selects" the dropdown and attaches handlers to the change and keypress events. When they fire, it calls the displayAmount function. displayAmount() retrieves the selected index, then grabs the value out of the list. Finally it sets the HTML to the amount retrieved.
Instead of the local array, you could call your controller. You would create an action (method) on your controller that returned the value as a JsonResult. You would do a callback using jquery.ajax(). Do some searching here and the jQuery site, I'm sure you'll find a ton of examples on how to do this.
I have a div like this
<section class="item_container">
<article class="item">
<h2>Page Title</h2>
<p><a class="delete_page" href="http://www.abc.com/delete/1/">delete</a></p>
</article>
<article class="item">
<h2>Second Page Title</h2>
<p><a class="delete_page" href="http://www.abc.com/delete/2/">delete</a></p>
</article>
</section>
And i am using this code to remove/delete items from item_container
$('a.delete_page').live('click', function(e){
e.preventDefault();
$('footer#ajax_footer').html('')
$('footer#ajax_footer').show("slide", { direction: "down" }, 500);
var url = $(this).attr("href");
var parent_div = $(this).parents("div").parent("article");
var title = $(this).parents("div").parent("article").find('h2').html();
$('footer#ajax_footer').html('<h2>Are you sure you want to delete <u>'+ title +'</u> Page</h2><p>'+ url +'</p><p>Yes, Please Delete It</p>');
$('a.delete_this_page').live('click', function(e){
e.preventDefault();
parent_div.remove();
$('footer#ajax_footer').html('').slideUp();
$.sticky('<b>Page Deleted</b><p><u>'+ title +'</u> Page has been successfully deleted.</p>');
});
});
All the contents are loaded via Ajax and there are few more div in each article and there are 30+ articles.
The problem is i am using Sticky plugin to display notification after every item is deleted and it's working fine, Everything is working fine, But after i delete an Article, The sticky notification of previous deletion is displayed as well.
Like, After i delete an item, i see 1 sticky notification, After i delete second item i see 2 sticky notifications (1 of this and 1 of previous) i only want to see 1, And For every item i delete it display all the previous sticky notifications + 1.
Hope i made it clear enough, Thanks guys.
The problem you have is that every time you click on a a.delete_page element you're setting up a new click event handler using .live(), so for each subsequent click you get one more event handler that gets run.
The whole point of .live() - though, as a side note, it's deprecated; consider using .on() (jQuery 1.7+) or .delegate() (prior to 1.7) - is that it handles events triggered by dynamically added elements. Call this:
$('a.delete_this_page').live('click', function(e){
e.preventDefault();
parent_div.remove();
$('footer#ajax_footer').html('').slideUp();
$.sticky('<b>Page Deleted</b><p><u>'+ title +'</u> Page has been successfully deleted.</p>');
});
outside of your other callback function (in your $(document).ready()), and find another way to identify parent_div. Or, bind the click event to that specific link directly inside the callback handler.