Bootstrap tooltip/popover not showing properly - javascript

I'm using Angular 2 to build my web application. When I try to put a tooltip or popover on an image or button, I get the default-looking tooltip instead of the bootstrap one.
I've tried the basic W3Schools examples and did everything as shown, yet it doesn't work.
I believe my problem lies with the correct imports of the bootstrap.js or jQuery, but other bootstrap items like buttons etc. do work properly.
(I use nodejs to install the necessary files/dependencies -> npm install npm install bootstrap npm install jquery)
index.html
<html>
<head>
<base href="/"><!--Without this tag, the browser may not be be able to load resources (images, css, scripts) when "deep linking" into the app. Bad things could happen when someone pastes an application link into the browser's address bar or clicks such a link in an email link. -->
<title>Factory</title>
<link type="text/css" rel="stylesheet" href="/css/styles.css">
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<script src="node_modules/angular2/bundles/router.dev.js"></script>
<script src="/node_modules/angular2/bundles/http.dev.js"></script>
<!--<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">-->
<!--<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>-->
<!--<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>-->
<!--<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />-->
<!-- stackoverflow suggested settings -->
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app/boot')
.then(null, console.error.bind(console));
</script>
</head>
<body>
<my-app></my-app>
</body>
</html>
page-with-tooltip(.ts)
import {Component} from "angular2/core";
import ... from ...;
import ... from ...;
...
#Component({
template: `
<div *ngIf="hall" class="container">
<h2>Detail van hal <small>{{hall.name}}</small></h2>
Hover over me
<div class="container">
<div class="hall-box-single">
<div class="hall-box"
[style.width]="hall.areaWidth*4"
[style.height]="hall.areaHeight*4">
<div class="image-container">
<div *ngFor="#item of hall.items">
<a data-toggle="tooltip" title="Popover title">
<img [src]="item.image"
[style.left]="item.posX"
[style.top]="item.posY"/>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function(){
$('[data-toggle="tooltip"]').tooltip();
});
</script>
`})
export class HallDetailComponent implements OnInit {
...
constructor(...) {
...
}
ngOnInit() {
...
(<any>$('[data-toggle="tooltip"]')).tooltip();
$('[data-toggle="tooltip"]').tooltip();
}
}
!Edit!
Added suggestions from users as well as an extract of the running webpage source.

The script tag you wrote in your template is getting ignored, run your JS code from inside the component instead, I suggest you to run it in the ngOnInit() method since you are implementing the OnInit interface.
This should work:
#Component({
selector: 'app',
template: `
Hover over me
`
})
export class AppComponent{
ngAfterViewInit(){
$('[data-toggle="tooltip"]').tooltip();
}
}
using:
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
So specific to your project:
You have three options to fix it:
One is to set a nasty timeout so it initiates the tooltips until you get a response back from the server and they have actually rendered.
The second is to find some method like the ones we try that gets executed everytime the dom changes, not just the first time.
The third and best but a bit more complicated is by implementing a method that gets executed before the page starts rendering, and if you return a promise in that method, the page will wait for the Promise to get done before rendering, so you can return a promise and resolve the promise until you get the answer from the service, that way the dom will be ready the first time the controller loads
I believe the last method is called CanActivate or something like that.

Related

Bootstrap navbar drop-down doesn't toggle

I want to use bootstrap navbar in my website and so i have made the webpage header.html (link given below) and it works fine
http://upscfever.com/upsc-fever/header.html
However when i use the same code on my home page (link below). it doesnt close after we open it.
http://upscfever.com/upsc-fever/index.html
On my homepage - index.html i have written below code.
<script>
$(function () {
$("#header").load("http://upscfever.com/upsc-fever/header.html");
});
</script>
<body>
<div id="header"></div>
<!-- other things-->
</body>
I get the error:
jQuery.Deferred exception: $(...).dropdown is not a function
#http://upscfever.com/upsc-fever/index.html:1:15
g/https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js:2:29946
g/https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js:2:30262
you may want to put the load script just before the end of the body tag, so that the <div id="header"> gets added to the DOM before using it.
Also make sure you are not loading bootstrap js more than once in your index.html file.
Maybe try with
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
and see if it will work.

Referencing same js files from multiple locations using partial views

I am setting up an architecture for an MVC6 app, and I'm relying heavily on ViewComponents. My goal is to let each ViewComponent have its own javascript section, but from reading here rendersection does not work with ViewComponents so I've been trying to do it in another way.
In my _Layout.cshtml
I have this part in just before the closing of the body tag:
#{Html.RenderPartial("_LayoutScriptsPartial"); }
In _LayoutScriptsPartial
<environment names="Development">
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/js/app.js" asp-append-version="true"></script>
</environment>
<environment names="Staging,Production">
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js">
</script>
<script src="~/js/app.min.js" asp-append-version="true"></script>
</environment>
Now in one of my components at /Views/Shared/Components/MyComponent/Default.cshtml I reference another partial view that has this content, it's for a carousel ViewComponent
#model MyProj.MyViewModel
#{Html.RenderPartial("_LayoutScriptsPartial"); }
#if (!string.IsNullOrEmpty(Model.ImageUrl))
{
<script type="text/javascript">
var cssClass= "#Model.cssClass";
var imageUrl = "#Model.ImageUrl";
webbCore.carouselSetBackgroundImage(cssClass, imageUrl);
</script>
}
Only reason I had to do this was to have all required js files available for my view component.
As you can see, I reference _LayoutScriptsPartial multiple times. When I debug using chromes f12 and watching the network section, I do not se the same javascript files being downloaded multiple times. Still I have a bad feeling about this solution. I have looked around and have not found any good solutions for working with js files and ViewComponents that I really liked. Something like this would suit my needs.
My question: how good is this solution, whats the pros and cons and are there any better ways to work with js files and ViewComponents?
There are 3 main cases for an ideal solution to your problem:
ViewComponent is added 0 times and the corresponding JavaScript library is added 0 times
ViewComponent is added 1 time, the corresponding JavaScript library is added 1 time and the dynamically created initialization JavaScript is created once and placed after the library.
ViewComponent is added many times, the corresponding JavaScript library is added 1 time and the dynamically created initialization JavaScript is created for each ViewComponent and are all placed after the library.
So in your example, jquery and app.js are your libraries while your dynamically created initialization JavaScript is the part that references #Model in your <script type="text/javascript"> tag. Let's say we added your component to a view 3 times (I'll replace #RenderBody() with the resulting html from a view that invokes your component 3 times):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>#ViewData["Title"] - MyProj.Web</title>
<environment names="Development,Staging,Production">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/lib/font-awesome/css/font-awesome.css" />
<link rel="stylesheet" href="~/css/site.css" />
<link rel="stylesheet" href="~/css/culture-flags.css" />
</environment>
</head>
<body>
<header>#Html.Partial("~/Views/Shared/_Header.cshtml")</header>
<main>
<nav></nav>
<article>
<div class="container body-content">
<!--#RenderBody()-->
<!--#await Component.InvokeAsync("MyComponent", new MyViewModel {cssClass="t1", ImageUrl="1.jpg"})-->
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/js/app.js" asp-append-version="true"></script>
<script type="text/javascript">
var cssClass= "t1";
var imageUrl = "1.jpg";
webbCore.carouselSetBackgroundImage(cssClass, imageUrl);
</script>
<!--#await Component.InvokeAsync("MyComponent", new MyViewModel {cssClass="t2", ImageUrl="2.jpg"}) -->
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/js/app.js" asp-append-version="true"></script>
<script type="text/javascript">
var cssClass= "t2";
var imageUrl = "2.jpg";
webbCore.carouselSetBackgroundImage(cssClass, imageUrl);
</script>
<!--#await Component.InvokeAsync("MyComponent", new MyViewModel {cssClass="t3", ImageUrl="3.jpg"}) -->
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/js/app.js" asp-append-version="true"></script>
<script type="text/javascript">
var cssClass= "t3";
var imageUrl = "t3.jpg";
webbCore.carouselSetBackgroundImage(cssClass, imageUrl);
</script>
</div>
</article>
<aside></aside>
</main>
<footer>#Html.Partial("~/Views/Shared/_Footer.cshtml")</footer>
<!-- These are the libraries that should only be loaded once -->
<environment names="Development,Staging,Production">
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
</environment>
<!-- Ideally this is where the dynamically create scripts would go -->
#RenderSection("scripts", required: false)
</body>
</html>
As you can see, you would be loading the jquery library 4 times, once for your Layout and 3 times for your ViewComponents. Any good browser should only download the file once but will be loaded into memory multiple times and will just overwrite the the same global variables multiple times($ for example).
You might also be tempted to move the library to the top of the Layout and remove the references from the View Component but that is not a best practice either.
The main issue is that section doesn't work with ViewComponents and that appears to be by design. You should think of a ViewComponent as a fancy html helper. I haven't seen any great solutions to this problem but here are a couple of ideas.
Within the View, immediately after you call your component (Component.InvokeAsync("MyComponent")), add your javascript to #section scripts {...}
Create a library js that initializes this component and set data attributes from the element
$(".carousel").each(function() {
var css = $(this).data("carousel-css");
var image = $(this).data("carousel-image");
});
<input class="carousel" type=hidden data-carousel-css="#Model.cssClass" data-carousel-image="#Model.imageURL" />

Running angular partial in a new tab

I am presenting my issue in a simple manner as below
There is a index.html as below
Index.html
<script src="js/libs/jquery/jquery.js"></script> <!--JQuery library-->
<script src="js/libs/angularjs/angular.js"></script>
<script src="js/libs/angularjs/app.js"></script>
{{title}}<br>
<ng-include src="'document.html'"></ng-include><br>
newPage
My app.js is as follows
angular.module('myApp',[])
.controller('myController',function($scope){
//array to store the items
$scope.title = "This is a test message";
$scope.secondPage = "this is the second page"
});
The document.html is as follows
<div ng-controller="myController">
{{secondPage}}
</div>
I need to run the document.html in a new page, but obviously since the document.html does not have the import script of angular.js,app.js it won't render. But if i do put the script statements in document.html there will be multiple imports in the index.html. How do i solve this issue ?
Require.js would not solve this issue.
One bad way to solve the issue would be import the document.html as text and remove the importing statements.(bad bad way !!)
Create a new page for the new window opener similar to index.html
So your code will be like:
Index.html
<script src="js/libs/jquery/jquery.js"></script> <!--JQuery library-->
<script src="js/libs/angularjs/angular.js"></script>
<script src="js/libs/angularjs/app.js"></script>
{{title}}<br>
<ng-include src="'document.html'"></ng-include><br>
newPage
documentwindow.html
<script src="js/libs/jquery/jquery.js"></script> <!--JQuery library-->
<script src="js/libs/angularjs/angular.js"></script>
<script src="js/libs/angularjs/app.js"></script>
{{title}}<br>
<ng-include src="'document.html'"></ng-include><br>
Hope this works for you

Meteor - Ckeditor Integration

I'm not sure if there has been a change in the way Meteor loads items, or the way it handles jquery, but I'm having an awful lot of trouble getting ckeditor to come up.
Main Template (Iron-router):
<template name="layout">
<head>
<script type="text/javascript" src="js/ckeditor/ckeditor.js"></script>
<script type="text/javascript" src="js/ckeditor/adapters/jquery.js"></script>
</head>
.....
</template>
Independent Editor Template:
<template name="editor">
<div class="editor_container">
<textarea class="editor"></textarea>
</div>
</template>
Ckeditor located at public/js/ckeditor, any time I try to do the Template.editor.rendered() technique, or even just trying to type $('.editor').ckeditor(); into the console, I get an error of:
$('.editor').ckeditor();
VM48825:2 Uncaught TypeError: undefined is not a function
Any ideas?
Try taking the <head> section out of the layout template. Reading here I believe the <head> section is treated specially be meteor (see: http://docs.meteor.com/#/full/structuringyourapp) and that it being inside a template may be causing the JS to actually not be loaded. Just a guess though.
<head>
<script type="text/javascript" src="js/ckeditor/ckeditor.js"></script>
<script type="text/javascript" src="js/ckeditor/adapters/jquery.js"></script>
</head>
<template name="layout">
.....
</template>
You can use IRLibLoader from iron:router into the onBeforeAction like this.
Router.route('/editor', {
name: 'editor',
template: 'layout',
onBeforeAction: function () {
var ckEditor = IRLibLoader.load('/js/ckeditor/ckeditor.js');
var adapter = IRLibLoader.load('/js/ckeditor/adapters/jquery.js');
if(ckEditor.ready() && adapter.ready()){
console.log('The 2 JS just finish load');
this.next(); // Render the editor page
if(Meteor.isClient){
Template.editor.rendered = function(){
$('.editor').ckeditor();
console.log("loading coeditor when template fully rendered");
}
}
}
}
});
Alternative on the main layout you can use this.
<head>
<script type="text/javascript" src="js/ckeditor/ckeditor.js"></script>
<script type="text/javascript" src="js/ckeditor/adapters/jquery.js"></script>
</head>
<template name="layout">
{{> yield}}
</template>
<template name="editor">
<div class="editor_container">
<textarea class="editor"></textarea>
</div>
</template>
And do the same rendered function
Template.editor.rendered = function(){
$('.editor').ckeditor();
//or make a little delay (1sec)
Meteor.setTiemout(function(){
$('.editor').ckeditor();
},100)
}
There are several problems with your code :
You can't put <head> sections inside another template, it must be done outside all templates.
The path to your JS files are broken, you must prepend a slash to them to reference files in the public directory.
Loading scripts in <head> sections is not a good idea because they will be loaded when your app first loads for every user, even if they never use the editor.
Here is a solution where we load every scripts asynchronously using jQuery promises when the editor template is rendered, and only then initialize the CKEditor.
Template.editor.rendered=function(){
var template=this;
$.when(
$.getScript("/js/ckeditor/ckeditor.js"),
$.getScript("/js/ckeditor/adapters/jquery.js")
).done(function(){
template.$(".editor").ckeditor();
});
};

What is the cause for "angular is not defined"

I'm following the video tutorials on egghead.io but while trying to follow his example when he created a factory (see video here) I keep getting "angular is not defined" Reference Error but I have included the angular script
This is my html page:
<!DOCTYPE html>
<html>
<head>
<title>Prototype</title>
<link rel="stylesheet" type="text/css" href="foundation.min.css">
<script type="text/javascript" src="main.js"></script>
</head>
<body>
<div data-ng-app="">
<div data-ng-controller="FirstController">
<input type="text" data-ng-model="data.message">
<h1>{{ data.message }}</h1>
</div>
<div data-ng-controller="SecondController">
<input type="text" data-ng-model="data.message">
<h1>{{ data.message }}</h1>
</div>
</div>
<script type="text/javascript" src="angular.min.js"></script>
</body>
</html>
and this is my javascript file "main.js":
//Services
// step 1 create an app
var myApp = angular.module('Data', []).
// tep 2 create factory
// Service name, function
myApp.factory('Data', function(){
return { message: "I'm Data from a Service" }
});
//Controllers
function FirstController($scope, Data){
$scope.data = Data;
}
function SecondController($scope){
}
I have read a few posts where similar happen (here) and please correct me if I'm wrong but I think it is to do with Boot strapping andI have tried manually bootstrapping using angular.bootstrap(document, ['Data']); but with no success, still get same error.
But What I want to know is, Why this works for so many examples online, like the egghead video series, but I have issues as I believe I have followed his video very closely. is it a change in angular in recent versions?
You have to put your script tag after the one that references Angular. Move it out of the head:
<script type="text/javascript" src="angular.min.js"></script>
<script type="text/javascript" src="main.js"></script>
The way you've set it up now, your script runs before Angular is loaded on the page.
You have not placed the script tags for angular js
you can do so by using cdn or downloading the angularjs for your project and then referencing it
after this you have to add your own java script in your case main.js
that should do
I had the same problem as deke. I forgot to include the most important script: angular.js :)
<script type="text/javascript" src="bower_components/angular/angular.min.js"></script>

Categories

Resources