I have this render method in my parent view.
It appends a child view that has a TinyMCE instance.
render: function() {
this.$('#EditorContainer').append(new LibraryEditorView({
}).render().$el);
return this;
}
And here is that child view(LibraryEditorView).
It renders an instance of TinyMCE using setTimeout:
export default Backbone.View.extend({
template: _.template(LibraryEditor),
render: function () {
this.$el.html(this.template());
setTimeout(_.bind(this.loadEditor, this), 1);
return this;
},
loadEditor: function () {
console.log("LibraryEditorView.js >> loadEditor() ");
tinymce.init({
selector: '#NYCLibraryEditor'
});
}
});
The html template is simply this:
<div>
<textarea id="NYCLibraryEditor"></textarea>
</div>
When it first loads, it loads the TinyMCE instance fine.
However, if I navigate away from the page, and come back, it won't load the tinyMCE instance, just a blank textbox.
I do still see the LibraryEditorView.js >> loadEditor() message in the console.
I had read, that using setTimeout would fix this issue. But in my case, it hasn't.
Is there anything else that might help?
Thanks!
Related
i'm trying to get angularjs to work with jquery masonry. Without angular i have managed to get masonry to work fine including css. However, i have used angular to script the whole web app and i now load items dynamically via angular. Since masonry needs to be reloaded after dynamically adding data, i have written a directive to listen to last element of the items in angular repeat so that i can call the jquery masonry. When i call masonry, for some reason the items gets all weird up and does not load when it loaded initially without ng-repeat. Please check the code given below and tell me what im doing wrong here. I wish i had plunkr/js fiddler but since i pull data from a backend i cannot replicate the same.
The library i use is called "masonry.pkgd.js"
<div id="container" class="masonry js-masonry" data-masonry-options='{ "columnWidth": ".grid-sizer", "itemSelector": ".item", "isFitWidth": true }'>
<div class="grid-sizer"></div>-
<div ng-repeat="item in items" class="{{item.class}}" newsitemupdated>
<?php $this->load->view('item') ?>
</div>
</div>
app.directive('newsitemupdated', function ($timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
$timeout(function () {
masonryUpdate();
});
}
}
}
});
var masonryUpdate = function() {
var $container = $('#container').masonry({
columnWidth: '.grid-sizer',
itemSelector: '.item',
isFitWidth: true
});
setTimeout(function() {
// $('#container').masonry();
$container.masonry('reloadItems');
console.log('called');
}, 500);
}
This is how it looks below i reload items.
This is how it looks after i reloaditems
Please tell me what im doing wrong and how i can call reload items after ng-repeat to load jquery masonry how its supposed to load.
I have fixed by doing the following
First i write a function to initialize my masonry with options
function BuildMasonry() {
var $container = $('#container').masonry();
$container.masonry({
columnWidth: '.grid-sizer',
itemSelector: '.item',
isFitWidth: true
});
}
Afterwards i will call it when ng-repeat finishes loading.
var masonryUpdate = function() {
$('#container').masonry('reloadItems');
BuildMasonry();
}
Hope this helps to someone out there :)
My main overview page has a few partial views in it (that run on different links). Each of these partial views has an element that refers to a directive (expander).
My problem is if I go to a partial view that has that directive, and then go back to the main screen and to a different partial view, the directive won't run on the newest screen.
The partials are loaded with ajax calls, and removed using .remove() on the parent in the view.
I'm not sure where the problem would be, so here are some bits of code that might be relevant.
My expander directive:
angular.module("toolkit").directive("expander", ['$timeout', function($timeout)
{
function determine(scope, heightValue) {
if (scope.isExpanded) collapse(heightValue);
else expand(heightValue);
}
function expand(heightValue) {
//does expanding things
}
function collapse(heightValue) {
//does collapsing things
}
return {
restrict: 'A',
link: function (scope, elm, attrs) {
var height = 0;
scope.$watch(attrs.expanderHeight, function (value) {
height = value;
});
$timeout(
function () {
$(elm[0]).on('click', function () {
scope.$apply(determine(scope, height));
});
}
)
}
};
}]);
Html for partial views (they all have this same thing):
<div class="acct-sum round-a accordion-top">
<div expander expander-height="170" class="title">
<span class="ui-icon ui-icon-circle-plus"></span>
<div class="accordion-inner">
Irrelevant things here.
</div>
</div>
</div>
This directive runs in every partial view as long as its the first I go to. But after switching back from one view to another, it stops working. I've put breakpoints into it and it never hits it at all.
Any ideas? I can post more code if anyone will find it relevant.
Edit:
The partial views are loaded on click like so:
// Bind account summary links to load the accountHistory partial view.
$(".view-history").on("click", function (e) {
e.preventDefault();
hide();
var acctId = $(this).data("account");
var t = $("#body_content_wrapper");
t.append("<div class='progress' />");
// load the template from the server in lue of client-routing
util.load({
viewId: "accounthistory",
target: t,
action: util.config.baseUri + "accountHistory",
type: "POST",
data: { id: $(this).data("account") }
})
.complete(function (html) {
$(".progress").remove();
util.publish("telnot.views.showAccountHistory", { account: acctId });
});
});
I am using twitter bootstrap link. When the user clicks the link a bootstrap modal appears.
Now because of some bootstrap technical difficulties in modal rendering i need to seperate the link and put the modal out the navbar div.
So consider i have two separate div
<div id="linkDiv">
</div>
and
<div id="modalDiv">
</div>
Now i have only one View which makes a call to the server to get the collection
app.View.FriendRequestListView = Backbone.View.extend( {
templateModalLink: _.template($('#link').html()),
templateModal: _.template($('#modal').html()),
tagName: 'div',
initialize: function(){
this.friendRequestCollection = new app.Collection.FriendRequestCollection();
this.friendRequestCollection.bind("reset", this.render, this);
this.friendRequestCollection.fetch();
},
render: function() {
$(this.el).html(this.templateModalLink({
friendRequestCollection: this.friendRequestCollection}));
return $(this.el);
},
});
Than i can render only one div like following
var list = new app.View.FriendRequestListView();
$('#linkDiv').html(list.$el);
My question is , Is it possible to render two templates at the same time and add the two templates to different DIV like for example in my case i want to get update
templateModalLink template to linkDiv and templateModal template to modalDiv with the collection I am getting from the server.
You have to instantiate the collection before app.View.FriendRequestListView(s) and pass app.View.FriendRequestListView(s) the collection:
var friendRequests = new app.Collection.FriendRequestCollection();
friendRequests.fetch(
success: function(collection, response, options) {
var list1 = new app.View.FriendRequestListView({collection: collection});
var list2 = new app.View.FriendRequestListView({collection: collection});
$('#linkDiv').html(list1.$el);
$('#modalDiv').html(list2.$el);
}
);
In the Marionette docs for CompositeView, it describes overriding appendHTML to customize the target the HTML of each itemView should be added to using jQuery's append.
Using a similar line of thinking, if I have two ItemView objects, a Parent and a Child, and Parent's template has an empty target <div class="target"></div> I'd like to inject Child's template inside of. I can use jQuery's html with an onRender in Parent to do this.
Throughout the course of the Parent's life, I want to flush out or replace the contents of .target with something else. If I later want to re-render Child into .target (since it may render it's template differently based on user interaction since the first rendering), I can use the same logic:
Render the Child template
Set .target's contents to Child's el with jQuery's html
The issue is, once this second rendering of Child has occurred, any events from the Child view seem to be lost.
Here is a concrete example of what I'm describing:
The HTML
<script type="template" id="parent">
<p><a class="re-render" href="javascript://">Re-Render</a></p>
<div class="target"></div>
</script>
<script type="template" id="child">
<p>The Link</p>
</script>
<div id="main"></div>
The Javascript
var Views = {};
Views.Child = Backbone.Marionette.ItemView.extend({
template: '#child',
events: {
'click a': 'changeBg'
},
changeBg: function() {
this.$el.css('background-color', '#'+Math.floor(Math.random()*16777215).toString(16));
}
});
Views.Parent = Backbone.Marionette.ItemView.extend({
template: '#parent',
childView: undefined,
ui: {
target: '.target'
},
events: {
'click .re-render': 'onRender'
},
initialize: function() {
this.childView = new Views.Child();
},
onRender: function() {
this.childView.render();
this.ui.target.html(this.childView.el);
this.childView.$el.css('background-color', 'transparent');
}
});
var App = new Backbone.Marionette.Application();
App.addRegions({
mainRegion: "#main"
})
App.mainRegion.show(new Views.Parent());
Clicking The Link in Child at first works great. Once Re-Render is clicked in Parent, the Child click event is never re-applied to the newly rendered version of the Child's template. How can I ensure the Child template's events will be re-applied each time the Child is rendered?
You can find a jsFiddle for this here.
You should call .delegateEvents() on the child view after it has been re-rendered:
onRender: function() {
this.childView.render();
this.ui.target.html(this.childView.el);
this.childView.delegateEvents();
this.childView.$el.css('background-color', 'transparent');
}
This is how to solve it in vanilla Backbone. I don't know if Backbone.Marionette has another way of handling the problem, but in Marionette terms you could add the delegateEvents call to the child view's onRender method to encapsulate the re-renderability.
Views.Child = Backbone.Marionette.ItemView.extend({
//...
onRender: function() {
this.delegateEvents();
}
});
I'm new with Backbone and I'm making an example app in which I have to include tabs. The thing is that I have a collection of cities and I want to create one tab for each city (the collection fetchs from the server). I made a view called TabsView, which in the render function passes the collection to a template, and this one loops through the collection and renders the tabs.
What I want to do is that the first tab appears as 'active'. What I've done for the moment is that each tab has a href to a route in the router which changes it's class to active using jquery. Don't know if this is the best way to do this but it works. Maybe there's a better way. Also, when the user clicks a tab, I want to be able to render other view.
Hope I made myself clear. Thanks, cheers,
Martin
Ok I solved this problem doing something like the following:
var Tabs = Backbone.View.extend({
template: JST['tabs'],
events: {
'click li' : 'switchTab'
},
tagName: 'ul',
className: 'nav-tabs',
render: function() {
this.renderTabs();
return this;
},
renderTabs: function() {
this.$el.html(this.template({ cities: this.cities }));
this.$('li:first').addClass('active');
},
switchTab: function(event) {
var selectedTab = event.currentTarget;
this.$('li.active').removeClass('active');
this.$(selectedTab).addClass('active');
}
});
It works fine, maybe it can be improved.