How combine enable popovers everywhere AND use container: 'body' option? - javascript

I noticed my Bootstrap 5 popovers are missing the arrow and I believe it's caused by the parent element interfearing somehow.
The docs are kind enough to offer help by showing me how to use the container: 'body' option - on ONE item. I need to apply that to all at once. How would I do that?
I'm using codeKit3 to bundle everything. The import statement looks like this (working):
// Bootstrap components
import '#popperjs/core/lib/index.js'
import bootstrap from 'bootstrap/dist/js/bootstrap.min.js'
This is the html I'm using:
<div class="card">
<div class="card-header">
<i class="fas fa-calendar-day"></i> 24-h
</div>
<div class="card-body">
<div class="row">
<div class="col-6">
högsta temp
</div>
<div class="col-3">
<span class="card-value" id="min-rh-24h">24</span><span class="card-value">°C</span>
</div>
<div class="col-3">
<a href="#" data-bs-toggle="popover" data-bs-placement="top"
title="Ytterligare information"
data-bs-content="Some sample text right here"><i
class="fas fa-info" id="maxCelsius24hInfo"></i></a>
</div>
</div>
<div class="row">
<div class="col-6">
lägsta temp
</div>
<div class="col-3">
<span class="card-value" id="min-rh-24h">12</span><span class="card-value">°C</span>
</div>
<div class="col-3">
<a href="#" data-bs-toggle="popover" data-bs-placement="top" data-container="body"
title="Ytterligare information"
data-bs-content="Some other sample data right here"><i
class="fas fa-info" id="minCelsius24hInfo"></i></a>
</div>
</div>
</div>
</div>
Code snippet from Bootstrap 5 docs:
var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'))
var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
return new bootstrap.Popover(popoverTriggerEl)
})
The only way the documentation shows how to implement the container option is like this:
var popover = new bootstrap.Popover(document.querySelector('.example-popover'), {
container: 'body'
})
I need to use the container: 'body' option in the first example (the one with map()). How would I do that?
Thank you!

var popover = new bootstrap.Popover(document.querySelector('.example-popover'), {
container: 'body'
})
var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
return new bootstrap.Popover(popoverTriggerEl)
})
This does the same basic method call new bootstrap.Popover(...) in both places - so just add the second parameter in the second version, exactly the same as it was used in the first one:
var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
return new bootstrap.Popover(popoverTriggerEl, { container: 'body' })
})

Related

In Knockout how do you go about nesting custom components when using an html binding?

I'm a bit new to knockout. I'm trying to get a custom component to dynamically load another custom component. I have a variable called location_board that contains html and that html has a custom component in it. . When I use the data-bind="html: location_board" it put the line for the in the dom but it doesn't run the custom component to fill out that node. Note: If I add the npc-widget directly to the template it works. It just doesn't work when it is added though the html binding. From my research I think this means I need to applyBindings on it? I'm not sure how to go about that in this situation though.
Any help is apricated.
Here is my full code for the custom component.
import {Database} from './database.js'
let database = new Database();
import {ResourceManager} from "./resource-manager.js";
let resourceManager = new ResourceManager();
let locationRegister = {
fog_forest: {
name: "The Ghostly Woodland",
image: "url('img/foggy_forest.jpeg')",
description: `
Place holder
`,
location_board: `
<npc-widget id="john-npc" params="id: 1, tree: 'shopkeep', speed: 50"></npc-widget>
<div>In</div>
`
}
};
ko.components.register('location-widget', {
viewModel: function (params) {
let self = this;
self.function = function () {
console.log("Functions!")
}
for(let k in locationRegister[params.id]) {
console.log(k)
this[k] = locationRegister[params.id][k];
}
console.log(this.name)
//return { controlsDescendantBindings: true };
},
template:
`
<div class="row">
<div class="col-lg-12">
<h2 id="title" class="tm-welcome-text" data-bind="html: name"></h2>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div data-bind="style: { 'background-image': image}" class="location-picture mx-auto d-block">
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="location-description mx-auto d-block" data-bind="html: description"></div>
</div>
</div>
</div>
<hr>
<div class="row">
<div class="col-lg-12">
<div id="location_board" data-bind="html: location_board">
</div>
</div>
</div>
`
});

Getting attribute from element

I made an object to manage the candidate education in online marketplace
let education ={
tabs:{},
render:function(){
document.querySelector('#education-tabs').innerHTML = ''
let container =``;
Object.values(this.tabs).map((tab)=>{
container += `
<div class="education-tab-container">
<div class="education-tab-right">
<div class="big-title">
${tab.faculty}
</div>
<div class="medium-title">
${tab.department}
</div>
<div class="small-title">
${tab.university}
</div>
</div>
<div class="education-tab-left">
<div class="date">
${tab.from || ''} ${tab.to || ''}
</div>
<div class="close" data-id="${tab.id}">
<span>x</span>
</div>
</div>
</div>
`
})
document.querySelector('#education-tabs').innerHTML = container
},
deleteTab:function(id){
delete this.tabs[id];
this.render();
},
add:async function(payload){
this.tabs = {...this.tabs,[payload.id]:payload}
this.render();
}
}
This updates the UI every time I add a new tab to this object.
The problem is when I try to remove a tab: sometimes I get the data-id attribute of the tab that I made and sometimes it returns null.
here is my function
const triggerListener = ()=>{
$("#education-tabs").find(".education-tab-left .close").click((e)=>{
if(e.target.getAttribute('data-id')){
alert(e.target.getAttribute('data-id'))
}
});
so is there a way to solve this problem?

How to add additional url in Vue JS

Hi I am using django and Vue to make an webapp. I am rendering data using v-for and fetching the data from a restful API using XMLHttprequest
This is my custom js file. I am not using Vue CLI I am using a CDN:
$(document).ready(function () {
function fetchData(){
Vue.component('postcontainer', {
props:['obj'],
template:
`<div>
<div v-for="data in obj" class="ui mt-3 card">
<div class="content">
<div class="right floated meta">{{data.date_posted}}</div>
<img class="ui avatar image" src="https://cdn.pixabay.com/photo/2017/08/30/12/45/girl-2696947_960_720.jpg">
{{data.author}}
</div>
<div v-if="data.image" class="image">
<img v-bind:src="[[data.image]]">
</div>
<div v-if="data.video">
<video controls v-bind:src="[[data.video]]" class="embed-responsive"></video>
</div>
<div class="content">
<div v-if="data.title">
<a class="text-dark" :href="data.id"><h3 class="header">{{data.title}}</h3></a>
</div>
<div class="description my-2">
<div v-if="data.caption">
<a class="text-dark" :href="data.id"><p>{{data.caption}}</p></a>
</div>
</div>
</div>
<div class="extra content">
<span class="right floated">
<i class="heart outline like icon"></i>
17 likes
</span>
<a :href="data.id"><i class="comment icon"></i></a>
</div>
</div>
</div>
`
})
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
var app = new Vue({
el: '#postCard',
component:['postconatiner'],
data: {
obj: JSON.parse(this.responseText),
}
})
}
}
xhr.open('GET', '/data/', true);
xhr.LOADING
xhr.send()
}
fetchData()
});
I want add an additional url before data.id example - ":href = 'detail/data.id'". My problem is when I try to add this the url becomes 'localhost:8000/NaN'. Please some help me. Also I want to if it's the right way to render html using VueJS.
Use the binded property href like this:
:href="'details/'+data.id"

Issues rendering handlebars to HTML

When I render my handlebars template in html, it looks like it's essentially skipping filling in the "handle bars" portion. I'm essentially printing messages with a title and content, and I'm using a "!each" helper to display all of my messages. I originally thought it was because it was because it was escaping the html around it, so I tried using a triple handle bar {{{ on each part however using the each helper with the triple stash gave me an error. Am I possibly using the handlebars incorrectly?
the typescript I used to render the HTML and my handlebars template is below:
public static refreshData(data: any) {
$("#indexMain").html(Handlebars.templates['main.hbs'](data));
//helper function for upvote button
Handlebars.registerHelper('getUButton', function (id) {
id = Handlebars.escapeExpression(id);
return new Handlebars.SafeString(
"<button type='button' class='btn btn-default up-button' id='u" + id + "'>Upvote</button>"
);
});
//helper function for downvote button
Handlebars.registerHelper("getDButton", function (id) {
id = Handlebars.escapeExpression(id);
return new Handlebars.SafeString(
"<button type='button' class='btn btn-default down-button' id='d" + id + "'>DownVote</button>"
);
});
// Grab the template script
var theTemplateScript = $("#main-template").html();
// Compile the template
var theTemplate = Handlebars.compile(theTemplateScript);
//get messages from server and add them to the context
// This is the default context, which is passed to the template
var context = {
messages: data
}
console.log("context:")
console.log(context);
// Pass data to the template
var theCompiledHtml = theTemplate(context);
console.log(theCompiledHtml);
// Add the compiled html to the page
$("#messages-placeholder").html(theTemplate(context));
//add all click handlers
//get all buttons with id starting with u and set the click listerer
$(".up-button").click((event) => {
var id = $(event.target).attr("id").substring(1);
main.upvote(id)
});
//get all buttons with id starting with d and set the click listerer
$(".down-button").click((event) => {
var id = $(event.target).attr("id").substring(1);
main.downvote(id)
});
}
<script id="main-template" type="text/x-handlebars-template">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Current Messages</h3>
</div>
<div class="panel-body">
<div class="list-group" id="message-list">
<!-- for each message, create a post for it with title, content, upvote count, and upvote button -->
{{#each messages}}
<li class="list-group-item">
<span class="badge">Vote Count: {{likeCount}}</span>
<h4 class="list-group-item-heading">{{title}}</h4>
<p class="list-group-item-text">{{content}}</p>
<div class="btn-group btn-group-xs" role="group" aria-label="upvote">
{{getUButton id}}
</div>
<div class="btn-group btn-group-xs" role="group" aria-label="downvote">
{{getDButton id}}
</div>
</li>
{{/each}}
</div>
</div>
</div>
</script>
<div id="messages-placeholder"></div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Post New Message</h3>
</div>
<div class="input-group">
<span class="input-group-addon">Title</span>
<input id="newTitle" type="text" class="form-control" placeholder="Title" aria-describedby="newTitle">
</div>
<div class="input-group">
<span class="input-group-addon">Message</span>
<input id="newMessage" type="text" class="form-control" placeholder="Message" aria-describedby="newMessage">
</div>
<div class="btn-group" role="group" aria-label="create">
<button type="button" class="btn btn-default" id="postNewMessage">Post Message</button>
</div>
<span class="label label-danger" id="incompleteAcc"></span>
</div>
Okay, then it is likely the data provided to your template is not in the correct form. Here's a working snippet (with non-essentials stripped out). The data passed to your refreshData template must be an array. Make sure it isn't an object containing an array.
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.js"></script>
</head>
<body>
<script>
let refreshData = (data) => {
// Grab the template script
var theTemplateScript = $("#main-template").html();
// Compile the template
var theTemplate = Handlebars.compile(theTemplateScript);
//get messages from server and add them to the context
// This is the default context, which is passed to the template
var context = {
messages: data
};
console.log("context:", context);
// Add the compiled html to the page
$("#messages-placeholder").html(theTemplate(context));
}
$(() => {
var data = [
{ likeCount: 3, title: 'My Title', content: 'Some content'},
{ likeCount: 0, title: 'My 2nd Title', content: 'Some other content'}
];
refreshData(data);
})
</script>
<script id="main-template" type="text/x-handlebars-template">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Current Messages</h3>
</div>
<div class="panel-body">
<div class="list-group" id="message-list">
<!-- for each message, create a post for it with title, content, upvote count, and upvote button -->
{{#each messages}}
<li class="list-group-item">
<span class="badge">Vote Count: {{likeCount}}</span>
<h4 class="list-group-item-heading">{{title}}</h4>
<p class="list-group-item-text">{{content}}</p>
</li>
{{/each}}
</div>
</div>
</div>
</script>
<div id="messages-placeholder"></div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Post New Message</h3>
</div>
<div class="input-group">
<span class="input-group-addon">Title</span>
<input id="newTitle" type="text" class="form-control" placeholder="Title" aria-describedby="newTitle">
</div>
<div class="input-group">
<span class="input-group-addon">Message</span>
<input id="newMessage" type="text" class="form-control" placeholder="Message" aria-describedby="newMessage">
</div>
<div class="btn-group" role="group" aria-label="create">
<button type="button" class="btn btn-default" id="postNewMessage">Post Message</button>
</div>
<span class="label label-danger" id="incompleteAcc"></span>
</div>
</body>
</html>
When I am faced with issues like this, I eliminate different things until I either get clarity or something I removed fixes the problem. Now I have isolated where the problem lies. In your situation, the issue is likely the data being passed so verify that. Then try stripping out your helpers to see if they are causing issues.

Search data on click event of button using smart table

I am very new to the smart table. I have gone through its documentation on Smart Table.
But the I haven't found how to bind data on click event in smart table?
Code is very big but I am trying to post it here.
<div class="table-scroll-x" st-table="backlinksData" st-safe-src="backlinks" st-set-filter="myStrictFilter">
<div class="crawlhealthshowcontent">
<div class="crawlhealthshowcontent-right">
<input type="text" class="crserachinput" placeholder="My URL" st-search="{{TargetUrl}}" />
<a class="bluebtn">Search</a>
</div>
<div class="clearfix"></div>
</div>
<br />
<div class="table-header clearfix">
<div class="row">
<div class="col-sm-6_5">
<div st-sort="SourceUrl" st-skip-natural="true">
Page URL
</div>
</div>
<div class="col-sm-2">
<div st-sort="SourceAnchor" st-skip-natural="true">
Anchor Text
</div>
</div>
<div class="col-sm-1">
<div st-sort="ExternalLinksCount" st-skip-natural="true">
External<br />Links
</div>
</div>
<div class="col-sm-1">
<div st-sort="InternalLinksCount" st-skip-natural="true">
Internal<br />Links
</div>
</div>
<div class="col-sm-1">
<div st-sort="IsFollow" st-skip-natural="true">
Type
</div>
</div>
</div>
</div>
<div class="table-body clearfix">
<div class="row" ng-repeat="backlink in backlinksData" ng-if="backlinks.length > 0">
<div class="col-sm-6_5">
<div class="pos-rel">
<span class="display-inline wrapWord" tool-tip="{{ backlink.SourceUrl }}"><b>Backlink source:</b> <a target="_blank" href="{{backlink.SourceUrl}}">{{ backlink.SourceUrl }}</a></span><br />
<span class="display-inline wrapWord" tool-tip="{{ backlink.SourceTitle }}"><b>Link description:</b> {{ backlink.SourceTitle }}</span> <br />
<span class="display-inline wrapWord" tool-tip="{{ backlink.TargetUrl }}"><b>My URL:</b> <a target="_blank" href="{{backlink.TargetUrl}}">{{ backlink.TargetUrl }}</a></span><br />
</div>
</div>
<div class="col-sm-2">
<div class="pos-rel">
{{ backlink.SourceAnchor }}
</div>
</div>
<div class="col-sm-1">
<div>
{{ backlink.ExternalLinksCount }}
</div>
</div>
<div class="col-sm-1">
<div>
{{ backlink.InternalLinksCount }}
</div>
</div>
<div class="col-sm-1">
<div ng-if="!backlink.IsFollow">
No Follow
</div>
</div>
</div>
<div class="row" ng-if="backlinks.length == 0">
No backlinks exists for selected location.
</div>
</div>
<div class="pos-rel" st-pagination="" st-displayed-pages="10" st-template="Home/PaginationCustom"></div>
</div>
and my js code is here.
module.controller('backlinksController', [
'$scope','$filter', 'mcatSharedDataService', 'globalVariables', 'backlinksService',
function ($scope,$filter, mcatSharedDataService, globalVariables, backlinksService) {
$scope.dataExistsValues = globalVariables.dataExistsValues;
var initialize = function () {
$scope.backlinks = undefined;
$scope.sortOrderAsc = true;
$scope.sortColumnIndex = 0;
};
initialize();
$scope.itemsByPage = 5;
var updateTableStartPage = function () {
// clear table before loading
$scope.backlinks = [];
// end clear table before loading
updateTableData();
};
var updateTableData = function () {
var property = mcatSharedDataService.PropertyDetails();
if (property == undefined || property.Primary == null || property.Primary == undefined || property.Primary.PropertyId <= 0) {
return;
}
var params = {
PropertyId: property.Primary.PropertyId
};
var backLinksDataPromise = backlinksService.getBackLinksData($scope, params);
$scope.Loading = backLinksDataPromise;
};
mcatSharedDataService.subscribeCustomerLocationsChanged($scope, updateTableStartPage);
}
]);
module.filter('myStrictFilter', function ($filter) {
return function (input, predicate) {
return $filter('filter')(input, predicate, true);
}
});
But It is working fine with the direct search on textbox.
but according to the requirement I have to perform it on button click.
Your suggestions and help would be appreciated.
Thanks in advance.
You can search for a specific row by making some simple tweaks.
add a filter to the ng-repeat, and filter it by a model that you will insert on the button click, like so: <tr ng-repeat="row in rowCollection | filter: searchQuery">
in your view, add that model (using ng-model) to an input tag and define it in your controller
then pass the value to the filter when you click the search button
here's a plunk that demonstrates this
you can use filter:searchQuery:true for strict search
EDIT:
OK, so OP's big problem was that the filtered values wouldn't show properly when paginated, the filter query is taken from an input box rather then using the de-facto st-search plug-in, So I referred to an already existing issue in github (similar), I've pulled out this plunk and modified it slightly to fit the questioned use case.

Categories