Backbone.js audio control - javascript

I have a Backbone view, it uses an html template, placed on the page:
<script type="text/template" id="webphone-template">
<audio id="audio-ringtone" src="{% static "prime_app/assets/audio/ringtone.mp3" %}"></audio>
<div class="well">
<p class="text-muted text-center"><%= status %></p>
<div class="text-center">
<button id="btn-webphone-answer" class="btn btn-success">
<i class="fa fa-phone"></i>
</button>
<button id="btn-webphone-terminate" class="btn btn-danger">
<i class="fa fa-phone"></i>
</button>
</div>
</div>
</script>
My view has method ring(), which looks like this:
ring: function() {
$("#audio-ringtone").get(0).play();
}
But when this method is invoked audio doesn't play, no errors in JS console, the audio element is chosen correctly. What's the problem?

Well i dont know how your whole view works, if you add it i can modify the code, but i got it to work fairly easy. Again, i need to see your view code to see what youre doing wrong.
View = Backbone.View.extend({
template: _.template($('#webphone-template').html()),
events: {
'click #btn-webphone-answer': 'ring'
},
initialize: function(opt) {
// attach to the main body
$(opt['main']).append(this.el)
this.status = 'paused';
this.render();
},
ring: function() {
$(this.el).find('#audio-ringtone')[0].play()
},
render: function() {
$(this.el).append(this.template({status: this.status}));
return this;
}
});
new View({ main: $('body') });
works: http://jsfiddle.net/hLero5rd/

Finally, there was the following problem: audio-tag is in my view's element, when i call render(), it completely replaces all the HTML inside element, so if there is audio-element it just gets removed and the sound doesn't play anymore.

Related

How do you get a javascript function to recognize html code returned by an ajax request

I apologize ahead of time if this question has been addressed...I looked and did not see one. I am building a web app using flask. After successfully logging in the user is routed to index.html via the following code:
#blueprint.route('/index')
#login_required
def index():
return render_template('home/index.html', segment='index')
My index.html includes an ajax request which builds and returns custom html such as:
<div class="card" style="max-width: 22rem;">
<!-- Card content -->
<div class="card-body pb-3">
<!-- Title -->
<h4 class="card-title font-weight-bold">TailNumber</h4>
<h6 class="card-subtitle text-muted d-flex justify-content-between">
<p>AircraftType</p>
<p>Operator</p>
</h6>
<div class="collapse-content">
<div class="collapse" id="tail_number" style="">
<table class="table table-borderless table-sm mb-0">
<tbody>
<tr>
<td class="font-weight-normal align-middle">Engine</td>
<td class="float-right font-weight-normal">
<p class="mb-1">2.5<span class="text-muted">IPS</span></p>
</td>
</tr>
</tbody>
</table>
</div>
<hr class="">
<button class="btn" type="button" data-toggle="collapse" data-target="#tail_number"
aria-expanded="false" aria-pressed="false" aria-controls="tail_number">Show
</button>
</div>
</div>
</div>
I also include a javascript function designed to change the text of my "collapse" button from "Show" to "Hide" and back again as the content is expanded and collapsed:
$(document).ready(function() {
$('[data-toggle="collapse"]').click(function() {
$(this).toggleClass("active");
if ($(this).hasClass("active")) {
$(this).text("Hide");
} else {
$(this).text("Show");
}
});
});
While the collapse feature of my html code works the text is not being updated. If I hard code the html instead of loading it via my ajax request then the button text changes as advertised. My assumption is that the javascript above is not being applied to the html returned by the ajax request but I am not sure how to verify this or, more importantly, correct it. My simplified ajax request is here:
<script>
$(function(){
function update_fleet_view(){
// create an ajax request
var req = new XMLHttpRequest();
req.open("GET", "/ajax", true);
req.send();
req.onload = function () {
// update page with new fleet view
var data = JSON.parse(req.responseText);
document.getElementById("fleet_view").innerHTML = data["fleet_view"];
// timeout to run again after 1 minute
setTimeout(update_fleet_view, 60000);
}
}
// call the function to get it started
update_fleet_view();
})();
</script>
Event delegation was exactly what I needed...thanks for the link James.
I ended up with the following modifications to my JS:
$(document).ready(function() {
$("#fleet_view").on("click", '[data-toggle="collapse"]', function() {
if ($(this).hasClass("collapsed")) {
$(this).text("Hide");
} else {
$(this).text("Show");
}
});
});
where "fleet_view" is the "id" that is referenced by my ajax script.
I attempted to leave the $(this).toggleClass("active") in the script however I could never get it to toggle. Rather than spending precious time tracking that issue down I decided to look at the "collapsed" field as the click event toggles that automatically. I also had to modify my button to add the "collapsed" class otherwise the button text would not update for the first round of clicks (see code below). The "if/else" statement seems backwards but I assure you it works as intended. Again, I can only assume that the "collapsed" class gets added or removed after this script executes.
<button class="btn collapsed" type="button" data-toggle="collapse" data-target="#tail_number"
aria-expanded="false" aria-pressed="false" aria-controls="tail_number">Show
</button>

Inserting script for bootstrap popover

I have a bootstrap 4 popover with list inside which comes from a viewcomponent element (asp net core 3.1 project).
This popover is inside layout navigation bar (not sure if that's relevant) and after hovering over it the list should appear.
Now every list element appears correctly but after I click any of the list elements I want to send ajax post request but I cannot make my javascript work. I am not sure what is the problem. Is it because my script is only loaded when I hover on popover? Am I using events incorrectly? I tried a lot of variations to test if JS is working and seems like everything failed.
ViewComponent:
#model mymodel;
<div class='list-group'>
#foreach (var item in Model.Item)
{
<a href='#item.Url' class='btn list-group-item list-group-item-action mb-1'>You have been invited to join event: <b>#item.Name</b></a>
}
#foreach (var item in Model.AnotherItem)
{
<a id='lmao' href='#' onclick='doSomething(#item.Id);' class='list-group-item list-group-item-action mb-1'>You have been invited to join team: <b>#item.Name</b></a>
}
</div>
<script>
$('#lmao').click(function () { MyFunction(); return false; });
function MyFunction() {
alert('hello');
console.log(data);
console.log(this);
}
</script>
_Layout Page with popover:
<div class="navbar-collapse">
<ul class="navbar-nav">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-toggle="dropdown">
Settings
</a>
...
<a tabindex="0" class="dropdown-item pop" data-container="body" data-html="true" title="Your invitations" data-toggle="popover" data-placement="left" data-content="#await Component.InvokeAsync("InvitationList")" style="cursor: pointer;">Invitations (number coming from another viewcomponent)</a>
...
</div>
</li>
...
Popover JS in Layout:
<script>
$('.pop').popover({
trigger: 'manual',
html: true
})
.on('mouseenter', function () {
var _this = this;
$(this).popover('show');
$('.popover').on('mouseleave', function () {
$(_this).popover('hide');
});
}).on('mouseleave', function () {
var _this = this;
setTimeout(function () {
if (!$('.popover:hover').length) {
$(_this).popover('hide');
}
}, 300);
});
</script>
My popover works correctly, I see the items loaded from viewcomponent, I can press on each of them and go to preset href.
What doesn't work is javascript coming from viewcomponent or atleast I am failing to test it. My end goal is to make a post call if any of the items in the popover are clicked.
I tested your codes and found if you directly call viewcomponent in data-content, the jacascripts in ViewComponent will be recongnized as html, so it will never be called. Instead, you can define an area for viewcomponent separately
<a id="invitations" tabindex="0" class="dropdown-item pop" data-container="body" data-html="true" title="Your invitations" data-toggle="popover" data-placement="left" style="cursor: pointer;" data-content="">invitations</a>
<div id="vc" style="display:none">
#await Component.InvokeAsync("InvitationList")
</div>
Use js to append it to data-content
$("#invitations").attr("data-content", $("#vc").html())
Since it is a new element from other page, the handler won't be bound on that new element, your click function should be like this:
<script>
$('body').on('click', '#lmao', function () { MyFunction(); return false; });
function MyFunction() {
alert('hello');
console.log(data);
console.log(this);
}
</script>
Test result:

Using div element in data-content of Bootstrap Popover [duplicate]

I am trying to display HTML inside a bootstrap popover, but somehow it's not working. I found some answers here but it won't work for me. Please let me know if I'm doing something wrong.
<script>
$(function(){
$('[rel=popover]').popover({
html : true,
content: function() {
return $('#popover_content_wrapper').html();
}
});
});
</script>
<li href="#" id="example" rel="popover" data-content="" data-original-title="A Title">
popover
</li>
<div id="popover_content_wrapper" style="display: none">
<div>This is your div content</div>
</div>
You cannot use <li href="#" since it belongs to <a href="#" that's why it wasn't working, change it and it's all good.
Here is working JSFiddle which shows you how to create bootstrap popover.
Relevant parts of the code is below:
HTML:
<!--
Note: Popover content is read from "data-content" and "title" tags.
-->
<a tabindex="0"
class="btn btn-lg btn-primary"
role="button"
data-html="true"
data-toggle="popover"
data-trigger="focus"
title="<b>Example popover</b> - title"
data-content="<div><b>Example popover</b> - content</div>">Example popover</a>
JavaScript:
$(function(){
// Enables popover
$("[data-toggle=popover]").popover();
});
And by the way, you always need at least $("[data-toggle=popover]").popover(); to enable the popover. But in place of data-toggle="popover" you can also use id="my-popover" or class="my-popover". Just remember to enable them using e.g: $("#my-popover").popover(); in those cases.
Here is the link to the complete spec:
Bootstrap Popover
Bonus:
If for some reason you don't like or cannot read content of a popup from the data-content and title tags. You can also use e.g. hidden divs and a bit more JavaScript. Here is an example about that.
you can use attribute data-html="true":
<a href="#" id="example" rel="popover"
data-content="<div>This <b>is</b> your div content</div>"
data-html="true" data-original-title="A Title">popover</a>
Another way to specify the popover content in a reusable way is to create a new data attribute like data-popover-content and use it like this:
HTML:
<!-- Popover #1 -->
<a class="btn btn-primary" data-placement="top" data-popover-content="#a1" data-toggle="popover" data-trigger="focus" href="#" tabindex="0">Popover Example</a>
<!-- Content for Popover #1 -->
<div class="hidden" id="a1">
<div class="popover-heading">
This is the heading for #1
</div>
<div class="popover-body">
This is the body for #1
</div>
</div>
JS:
$(function(){
$("[data-toggle=popover]").popover({
html : true,
content: function() {
var content = $(this).attr("data-popover-content");
return $(content).children(".popover-body").html();
},
title: function() {
var title = $(this).attr("data-popover-content");
return $(title).children(".popover-heading").html();
}
});
});
This can be useful when you have a lot of html to place into your popovers.
Here is an example fiddle: http://jsfiddle.net/z824fn6b/
You need to create a popover instance that has the html option enabled (place this in your javascript file after the popover JS code):
$('.popover-with-html').popover({ html : true });
I used a pop over inside a list, Im giving an example via HTML
<a type="button" data-container="body" data-toggle="popover" data-html="true" data-placement="right" data-content='<ul class="nav"><li><a href="#">hola</li><li><a href="#">hola2</li></ul>'>
You only need put data-html="true" in the link popover. Is gonna work.
This is an old question, but this is another way, using jQuery to reuse the popover and to keep using the original bootstrap data attributes to make it more semantic:
The link
<a href="#" rel="popover" data-trigger="focus" data-popover-content="#popover">
Show it!
</a>
Custom content to show
<!-- Let's show the Bootstrap nav on the popover-->
<div id="list-popover" class="hide">
<ul class="nav nav-pills nav-stacked">
<li>Action</li>
<li>Another action</li>
<li>Something else here</li>
<li>Separated link</li>
</ul>
</div>
Javascript
$('[rel="popover"]').popover({
container: 'body',
html: true,
content: function () {
var clone = $($(this).data('popover-content')).clone(true).removeClass('hide');
return clone;
}
});
Fiddle with complete example:
http://jsfiddle.net/tomsarduy/262w45L5/
This is a slight modification on Jack's excellent answer.
The following makes sure simple popovers, without HTML content, remain unaffected.
JavaScript:
$(function(){
$('[data-toggle=popover]:not([data-popover-content])').popover();
$('[data-toggle=popover][data-popover-content]').popover({
html : true,
content: function() {
var content = $(this).attr("data-popover-content");
return $(content).children(".popover-body").html();
},
title: function() {
var title = $(this).attr("data-popover-content");
return $(title).children(".popover-heading").html();
}
});
});
On the latest version of bootstrap 4.6, you might also need to use sanitize:false for adding complex html.
$('.popover-with-html').popover({ html : true, sanitize : false })
I really hate to put long HTML inside of the attribute, here is my solution, clear and simple (replace ? with whatever you want):
<a class="btn-lg popover-dismiss" data-placement="bottom" data-toggle="popover" title="Help">
<h2>Some title</h2>
Some text
</a>
then
var help = $('.popover-dismiss');
help.attr('data-content', help.html()).text(' ? ').popover({trigger: 'hover', html: true});
You can change the 'template/popover/popover.html' in file 'ui-bootstrap-tpls-0.11.0.js'
Write: "bind-html-unsafe" instead of "ng-bind"
It will show all popover with html.
*its unsafe html. Use only if you trust the html.
For Bootstrap >= 5.2
To enable HTML content in Popovers: data-bs-html="true"
Example:
<a href="#"
data-bs-toggle="popover"
data-bs-title="A Title"
data-bs-html="true"
data-bs-content="This is <strong>bold</strong>">popover</a>
Doc: https://getbootstrap.com/docs/5.3/components/popovers/#options
You can use the popover event, and control the width by attribute 'data-width'
$('[data-toggle="popover-huongdan"]').popover({ html: true });
$('[data-toggle="popover-huongdan"]').on("shown.bs.popover", function () {
var width = $(this).attr("data-width") == undefined ? 276 : parseInt($(this).attr("data-width"));
$("div[id^=popover]").css("max-width", width);
});
<a class="position-absolute" href="javascript:void(0);" data-toggle="popover-huongdan" data-trigger="hover" data-width="500" title="title-popover" data-content="html-content-code">
<i class="far fa-question-circle"></i>
</a>
Actually if you're using Bootstrap5 with Django then their method of passing in content as a string is perfect and in line with Django's template inclusion. You can create a template file with whatever partial HTML that you need, so for example, there is not X-editable for Bootstrap5 that seems to work, so maybe you'd want to make a line edit together with Ok|Cancel buttons as content. Anyway, this is what I mean:
<button data-bs-content="{% include './popover_content.html' %}" type="button" class="btn btn-lg btn-danger" data-bs-toggle="popover" title="Popover title" >
Click to toggle popover
</button>
Where my settings.py templates section looks like this:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True, # True is necessary for django-bootstrap5 to work!
'OPTIONS': {
'debug': True,
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
I keep my templates (of every single app) in a <project dir>/templates/<app name> folder. I have MyMainApp/popover_content.html right beside MyMainApp/home.html wher the above example code was tested. But if you keep your templates in each app's Django folder, then you'll need to add "MyApp/templates" to the TEMPLATES[0]{'DIRS': ['MyApp/templates', 'MyApp2/templates']} list.
So at least this will give you the ability to put your popover HTML in the usual, syntax-highlighted Django template format, and makes good use of modularizaton of your Django template into components.
I'm personally going to use it to make an editable label (title and description fields of some data in my app).
One drawback is that if you use doublequotes (") when including: "{% include './popover_content.html' %}", then you must use single quotes all throughout the popover_content.html` template.
You also need to enable html for popovers, so your site-wide popover initializer would go:
<script type="text/javascript">
$(document).ready(() => {
var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'))
var popoverList = popoverTriggerList.map(
function (popoverTriggerEl) {
return new bootstrap.Popover(popoverTriggerEl, {
html: true,
});
});
});
</script>
Here is the (unstyled) result. In conclusion, use the default-provided string method of passing in, and pass in an included Django template file. Problem solved!

Meteor using different Sessions with {{#each}} for click event

I want to use different Sessions for a specific click event within a {{#each}} block. The problem is that HTML elements will be displayed for all {{#each}} block members, but I only want it for the one, a user performs the click event on.
Here is my code:
...
{{#each articles}}
<tr id="{{_id}}" class="article-row">
<td class="articles">
{{> article}}
</td>
</tr>
{{/each}}
...
<template name="article">
{{#if editArticle}}
<div class="input-group">
<span class="input-group-addon">Edit article</span>
<input type="text" name="edit-article-input" class="form-control" placeholder="Name your article." value="{{title}}">
<span class="input-group-btn">
<button class="btn btn-success glyphicon glyphicon-ok edit-article-submit" data-type="last"></button>
</span>
</div>
{{else}}
<div class="panel panel-default article">
<div class="panel-heading">
<h3 class="panel-title">{{title}}</h3>
</div>
<div class="panel-body">
<button class="glyphicon glyphicon-pencil btn btn-default btn-xs edit-article"></button>
</div>
</div>
{{/if}}
</template>
Helper methods and events:
Template.article.helpers({
editArticle: function() {
return Session.equals('editArticle', true);
}
});
In the template with the {{#each}} block I use this helper method:
'click .edit-article': function(e, t) {
e.preventDefault();
Session.set('editArticle', true);
}
Now the problem is, that if I click on the button .edit-article the {{#if editArticle}} block appears on every article. I just want it for the one I clicked on.
Any help would be greatly appreciated.
This is a perfect example of how Session isn't always the right tool for the job when it comes to template reactivity. Unless you actually need to know which article is being edited outside of the template, I'd strongly recommend using a ReactiveVar or ReactiveDict rather than a global variable like Session. It's a little more to write but, in my opinion, a much more encapsulated and elegant solution. Here's the outline of the code you'd need:
Template.article.created = function() {
this.isEditing = new ReactiveVar(false);
};
Template.article.events({
'click .edit-article': function(e, template) {
e.preventDefault();
template.isEditing.set(true);
},
submit: function(e, template) {
e.preventDefault();
template.isEditing.set(false);
}
});
Template.article.helpers({
editArticle: function() {
return Template.instance().isEditing.get();
}
});
Note that you'd need to do meteor add reactive-var for this to work. For more details, see my post on scoped reactivity.

Backbone.js click event in a Modal Reveal

I got a Modal Reveal (http://foundation.zurb.com/docs/components/reveal.html) in a template and in this modal I got a form with a button and I want to listen to this button in the view but when I click it doesn't fire anything.
here is my code :
index.html :
<body>
<div class="page">
<div id="content"></div>
</div>
</body>
tplGroups.html :
<div id="myModal" class="reveal-modal small" data-reveal>
<label>Name :</label>
<input type="text" id="inNameGroup"></input>
<label > Description :</label>
<textarea id="inDescriptionGroup"></textarea>
<button class="button right" id="btnAddGroup">Add group</button>
<a class="close-reveal-modal">×</a>
</div>
<div class="row">
<button class="button tiny right" data-reveal-id="myModal" data-reveal>Add</button>
</div>
group.js :
el: "#content",
initialize: function(){},
render:function(){
this.template = _.template(tpl.get('tplGroup'));
this.$el.html(this.template(this.model.attributes));
this.$el.i18n();
return this;
},
events:{
"click #btnAddGroup": "addGroup"
},
addGroup: function(){
...
}
I think the problem is that the view can't find the button in the el.
Event's in backbone views work using event delegation, that is when you specify an events hash backbone binds those events to the root el. In this case your problem is that the modal popup isn't actually a descendent of the root el (the plugin attaches it elsewhere in the DOM, you can inspect the element in firebug/web inspector to see this yourself).
What you can do is bind the event manually yourself, for example
render:function(){
this.template = _.template(tpl.get('tplGroup'));
this.$el.html(this.template(this.model.attributes));
this.$el.i18n();
$('#btnAddGroup').on('click', this.addGroup);
return this;
},
Even though there has already been a correct answer I would like to add that if you open the modal from within the Backbone view it returns a reference to the new element.
var modalElement = this.$('#myModal').foundation('reveal', 'open');
The element's reference can be applied to the view using the setElement() method which will create the cached $el reference and move the view's delegated events from the old element to the new one.
tplGroups.html:
<div id="myModal" class="reveal-modal small" data-reveal>
<label>Name :</label>
<input type="text" id="inNameGroup"></input>
<label > Description :</label>
<textarea id="inDescriptionGroup"></textarea>
<button class="button right" id="btnAddGroup">Add group</button>
<a class="close-reveal-modal">×</a>
</div>
<div class="row">
<button class="button tiny right" id="btnOpenModal">Add</button>
</div>
group.js:
el: "#content",
initialize: function(){},
render:function(){
this.template = _.template(tpl.get('tplGroup'));
this.$el.html(this.template(this.model.attributes));
this.$el.i18n();
return this;
},
events:{
"click #btnOpenModal": "openModal",
"click #btnAddGroup": "addGroup"
},
openModal: function(){
this.setElement(this.$('#myModal').foundation('reveal', 'open'));
},
addGroup: function(){
...
}

Categories