我正在尝试让Bootstrap Native与Rails 5应用中的Turbolinks 5一起使用。当我第一次加载页面时,Bootstrap下拉菜单工作正常,但在导航到另一个页面后,Bootstrap下拉不再有效。就好像Bootstrap的事件监听器断开连接一样。
关于Bootstrap的jQuery实现,我已经看到了几个问题,但是,我有兴趣使用Bootstrap Native并从我的JS堆栈中消除jQuery。
以下是一些细节:
的application.js
import UIKit
class MainVC: UIViewController {
let setupMainVC = SetupMainVC()
var rightViewXConstant : NSLayoutConstraint!
var leftViewXConstant : NSLayoutConstraint!
var arrowLeftXConstant : NSLayoutConstraint!
var centerViewXConstant : NSLayoutConstraint!
var rightViewOpen = false
var leftViewOpen = false
var windowSize = CGFloat()
override func viewDidLoad() {
super.viewDidLoad()
let window = UIWindow.init(frame: UIScreen.main.bounds)
window.makeKeyAndVisible()
view.backgroundColor = UIColor.clear
setupMainVC.mainVC = self
setupMainVC.setupViews()
windowSize = window.frame.size.width
setupLeftViewController()
setupCenterViewController()
navigationItem.title = "Center View"
self.setupMainVC.arrowButtonLeft.transform = CGAffineTransform(rotationAngle: 0)
arrowLeftXConstant.constant = 20
print(windowSize)
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Left", style: .plain, target: self, action: #selector(leftButAction))
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Right", style: .plain, target: self, action: #selector(rightButAction))
setupMainVC.arrowButtonLeft.addTarget(self, action: #selector(leftButAction), for: .touchUpInside)
NotificationCenter.default.addObserver(self, selector: #selector(leftButAction), name: NSNotification.Name("leftButtonAction"), object: nil)
}
func leftButAction() {
print("Left Action")
if leftViewOpen {
leftViewOpen = false
centerViewXConstant.constant = 0
leftViewXConstant.constant = 0
self.setupMainVC.arrowButtonLeft.transform = CGAffineTransform(rotationAngle: 0)
arrowLeftXConstant.constant = 20
navigationItem.title = "Center View"
print(false)
} else {
leftViewOpen = true
centerViewXConstant.constant = windowSize
leftViewXConstant.constant = windowSize
self.setupMainVC.arrowButtonLeft.transform = CGAffineTransform(rotationAngle: -179)
arrowLeftXConstant.constant = -20
navigationItem.title = "Left View"
print(true)
}
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
func rightButAction() {
print("Right Action")
if rightViewOpen {
rightViewOpen = false
print(false)
} else {
rightViewOpen = true
print(true)
}
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
func center() {
let center = CenterVC()
addChildViewController(center)
center.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(center.view)
centerViewXConstant = center.view.centerXAnchor.constraint(equalTo: center.view.centerXAnchor, constant: 0)
centerViewXConstant.isActive = true
center.view.centerYAnchor.constraint(equalTo: center.view.centerYAnchor).isActive = true
center.view.heightAnchor.constraint(equalTo: center.view.heightAnchor).isActive = true
center.view.widthAnchor.constraint(equalTo: center.view.widthAnchor).isActive = true
center.view.backgroundColor = .blue
center.didMove(toParentViewController: self)
}
//MARK: Center View
func setupCenterViewController() {
let controller = CenterVC()
addChildViewController(controller)
controller.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(controller.view)
controller.view.addSubview(setupMainVC.roundViewOnLeftView)
controller.view.addSubview(setupMainVC.arrowButtonLeft)
centerViewXConstant = controller.view.leftAnchor.constraint(equalTo: setupMainVC.leftView.rightAnchor, constant: 0)
centerViewXConstant.isActive = true
controller.view.centerYAnchor.constraint(equalTo: setupMainVC.leftView.centerYAnchor).isActive = true
controller.view.heightAnchor.constraint(equalTo: setupMainVC.leftView.heightAnchor).isActive = true
controller.view.widthAnchor.constraint(equalTo: setupMainVC.leftView.widthAnchor).isActive = true
setupMainVC.roundViewOnLeftView.centerXAnchor.constraint(equalTo: controller.view.leftAnchor).isActive = true
setupMainVC.roundViewOnLeftView.centerYAnchor.constraint(equalTo: controller.view.centerYAnchor).isActive = true
setupMainVC.roundViewOnLeftView.heightAnchor.constraint(equalToConstant: 100).isActive = true
setupMainVC.roundViewOnLeftView.widthAnchor.constraint(equalToConstant: 100).isActive = true
arrowLeftXConstant = setupMainVC.arrowButtonLeft.centerXAnchor.constraint(equalTo: setupMainVC.roundViewOnLeftView.centerXAnchor, constant: 20)
arrowLeftXConstant.isActive = true
setupMainVC.arrowButtonLeft.centerYAnchor.constraint(equalTo: setupMainVC.roundViewOnLeftView.centerYAnchor).isActive = true
setupMainVC.arrowButtonLeft.heightAnchor.constraint(equalToConstant: 100).isActive = true
setupMainVC.arrowButtonLeft.widthAnchor.constraint(equalToConstant: 100).isActive = true
controller.didMove(toParentViewController: self)
}
//MARK: Left View
func setupLeftViewController() {
let controller = LeftVC()
//let navBar: UINavigationController = UINavigationController(rootViewController: controller)
addChildViewController(controller)
controller.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(controller.view)
leftViewXConstant = controller.view.rightAnchor.constraint(equalTo: setupMainVC.centerView.leftAnchor, constant: 0)
leftViewXConstant.isActive = true
controller.view.centerYAnchor.constraint(equalTo: setupMainVC.centerView.centerYAnchor).isActive = true
controller.view.heightAnchor.constraint(equalTo: setupMainVC.centerView.heightAnchor).isActive = true
controller.view.widthAnchor.constraint(equalTo: setupMainVC.centerView.widthAnchor).isActive = true
controller.didMove(toParentViewController: self)
}
}
应用程序布局
# app/assets/javascript/application.js
//= require turbolinks
//= require rails-ujs
//= require polyfill
//= require bootstrap-native
自举天然 这是Bootstrap Native package。
从我的Javascript标记中删除# views/layout/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title><%= full_title(yield(:title)) %></title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<%= render 'layouts/header' %>
<div class="container">
<%= yield %>
</div>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload', 'data-turbolinks-eval': 'false' %>
</body>
</html>
,从而在每个Turbolink导航上重新评估应用程序的所有Javascript,确实解决了Bootstrap Native问题,但它会导致rails-ujs抛出异常。< / p>
关于如何解决这个问题的任何想法?
答案 0 :(得分:2)
BSN开发人员在这里,想为什么不在BSN库外添加这个自定义代码,以便于维护?
var container = document.getElementById('myContainer');
document.addEventListener('turbolinks:load', function(){
container = container || document.getElementById('myContainer');
Array.prototype.forEach.call(container.querySelectorAll('[data-spy="affix"]'), function(element){ new Affix(element) });
Array.prototype.forEach.call(container.querySelectorAll('[data-dismiss="alert"]'), function(element){ new Alert(element) });
Array.prototype.forEach.call(container.querySelectorAll('[data-toggle="buttons"]'), function(element){ new Button(element) });
Array.prototype.forEach.call(container.querySelectorAll('[data-ride="carousel"]'), function(element){ new Carousel(element) });
Array.prototype.forEach.call(container.querySelectorAll('[data-toggle="collapse"]'), function(element){ new Collapse(element) });
Array.prototype.forEach.call(container.querySelectorAll('[data-toggle="dropdown"]'), function(element){ new Dropdown(element) });
Array.prototype.forEach.call(container.querySelectorAll('[data-toggle="modal"]'), function(element){ new Modal(element) });
Array.prototype.forEach.call(container.querySelectorAll('[data-toggle="popover"]'), function(element){ new Popover(element) });
Array.prototype.forEach.call(container.querySelectorAll('[data-spy="scroll"]'), function(element){ new ScrollSpy(element) });
Array.prototype.forEach.call(container.querySelectorAll('[data-toggle="tab"]'), function(element){ new Tab(element) });
Array.prototype.forEach.call(container.querySelectorAll('[data-toggle="tooltip"]'), function(element){ new Tooltip(element) });
},false);
您应该只查看特定的容器turbolinks更新,因为您不更新任何其他内容,并且您需要尽可能快。
我还更新了wiki page以包含一个通用示例。
<强>更新强>
从BSN版本2.0.20开始,您可以使用网站<head>
中的库,而无需任何其他脚本,并且您可以更轻松地执行此操作{/ 1}}:
turbolinks
如果您在控制台中输入var container = document.getElementById('myContainer');
document.addEventListener('turbolinks:load', function(){
container = container || document.getElementById('myContainer');
BSN.initCallback(container);
}, false);
按Enter键,则会收到以下对象:
BSN
请记住,您不必使用BSN = {
version: '2.0.20',
initCallback: function(lookup){},
supports: [ /* an array with supported components */ ]
}
作为您的turbolinks目标的ID属性,您可以使用任何内容使该元素唯一,我建议选择myContainer
。< / p>
下载最新的主人并尝试一下。
答案 1 :(得分:1)
使用Turbolinks时,您很可能希望将应用JavaScript文件放在<head>
中:
# views/layout/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title><%= full_title(yield(:title)) %></title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<%= render 'layouts/header' %>
<div class="container">
<%= yield %>
</div>
</body>
</html>
虽然这似乎违背了传统的最佳做法,但Turbolinks的性能优势在于脚本仅在初始页面加载时加载。
如果引导程序本机库在DOMContentLoaded
上初始化其插件/函数,您可能还需要在turbolinks:load
上手动调用这些函数,然后将其拆除(通常在turbolinks:before-cache
上)如果有必要的话。
答案 2 :(得分:1)
主要修订
在Bootstrap Native开发团队成员接受回答后,我修改了我在应用程序中实现的解决方案。我的解决方案与公认的解决方案略有不同,因为我的监听器将在整个DOM中搜索Bootstrap组件。相反,接受的解决方案只会在具有id="myContainer
的HTML标记内进行搜索。接受的解决方案将执行得更快,因为它只搜索DOM的子集。但是,它要求开发人员将相关的Bootstrap组件包装在myContainer
id的标记中。
两种解决方案都有效。我的解决方案运行速度会慢一点,但会导致编码更容易,并且不易受开发人员引发的错误的影响。以下是详细信息:
应用/资产/ JavaScript的/的application.js 强>
//= require turbolinks
//= require rails-ujs
//= require polyfill
//= require bootstrap-native
//= require bootstrap-native-turbolinks
应用/资产/ JavaScript的/自举天然-turbolinks.js 强>
document.addEventListener('turbolinks:load', function(){
Array.prototype.forEach.call(document.querySelectorAll('[data-spy="affix"]'), function(element){ new Affix(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-dismiss="alert"]'), function(element){ new Alert(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-toggle="buttons"]'), function(element){ new Button(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-ride="carousel"]'), function(element){ new Carousel(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-toggle="collapse"]'), function(element){ new Collapse(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-toggle="dropdown"]'), function(element){ new Dropdown(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-toggle="modal"]'), function(element){ new Modal(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-toggle="popover"]'), function(element){ new Popover(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-spy="scroll"]'), function(element){ new ScrollSpy(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-toggle="tab"]'), function(element){ new Tab(element) });
Array.prototype.forEach.call(document.querySelectorAll('[data-toggle="tooltip"]'), function(element){ new Tooltip(element) });
},false);
<强>供应商/资产/ Javascript角/ 强>
最后,我将我的应用程序javascript标记移动到<head>
并异步加载它,没有tuborlinks-eval和turbolinks-track。这会将Javascript配置为在初始页面加载时运行一次。在每个turbolinks页面访问时调用turbolinks:load
侦听器,并将Bootstrap Native事件侦听器附加到DOM中的相应组件。
应用/视图/布局/ application.html.erb 强>
...
<head>
...
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload', 'data-turbolinks-eval': 'false', async: true %>
</head>
...
在撰写本文时,此解决方案位于生产Rails 5.1应用程序中。
顺便提一下,连接和缩小时, application.js 中列出的所有Javascript都小于20KB。相比之下,jQuery本身缩小了86KB。 Bootstrap Native可以显着缩短应用的下载时间。
答案 3 :(得分:0)
这已通过 Vue.js 和 Laravel
进行了测试var bsn = require('bootstrap.native/dist/bootstrap-native-v4');
document.addEventListener('turbolinks:load', () => {
BSN.initCallback();
});