I have a data structure like this.
var myModel {
_id: 798698,
username: "John",
message: {
message1: "Some cool messsage",
message2: "I'm mad Ohio State lost"
}
}
The message object is a little tricky. Inside my handlebars template.
{{#each message}}
<div class="message">
{{../username}}: {{this}}
</div>
{{/each}}
Then when that renders the output is something like this.
<div class="NCAA">
<div class="NBA">
<div class="message">steve: What?</div>
<div class="message">steve: Okay?</div>
<div class="message">steve: Hey</div>
</div>
<div class="NBA"></div>
<div class="NBA">
<div class="message">
Iowastate: Hey nikeman
</div>
</div>
</div>
The class names come from a backbone view, but the problem is the template is being wrapped by a div and I am not sure how to prevent that so that its just a list of .message.. Also I cant identify why there is an empty div, I have suspicions but cant point my finger to it..
Here is the backbone view code, just to show you how things are rendered.
var Marionette = require('backbone.marionette');
var ChatView = Marionette.ItemView.extend({
className: 'NBA',
template: require('../../templates/message.hbs')
});
module.exports = CollectionView = Marionette.CollectionView.extend({
className: 'NCAA',
initialize: function() {
this.listenTo(this.collection, 'change', this.render);
},
itemView: ChatView
});
In Backbone, every view is associated with an el. At the time of instantiation, if you do not provide an el for the view, backbone uses an empty DIV. I guess this is what is happening in your case.
So at the time of your view instantiation, just provide an el element like this:
var myView = new ChartView({ el: $('.myElementForView') })
Read this text from Backbone View el documentation
All views have a DOM element at all times (the el property), whether they've already been inserted into the page or not. In this fashion, views can be rendered at any time, and inserted into the DOM all at once, in order to get high-performance UI rendering with as few reflows and repaints as possible. this.el is created from the view's tagName, className, id and attributes properties, if specified. If not, el is an empty div.
Related
I'm newbie in virtual DOM topic and last days I've been thinking of how it should work.
Let's imagine that I have a simple template engine in my project, for instance twig. And I'm using vue.js as a javascript framework.
So I could build the following page:
<div id="app">
<p>Some text</p>
<ul>
<li v-for="item in items">{{ item }}</li>
</ul>
<component><component/>
</div>
<template id="component">
<div class="center-xs">
<div class="row">
<div class="col-xs-12">
Some text
</div>
</div>
</div>
</template>
<script>
Vue.component('component', {
template: '#component'
});
var app = new Vue({
el: '#app',
data: {
items: ['item1', 'item2']
}
});
</script>
Which part of code would be Virtual DOM:
a. Nothing
b. Everything inside of #app
c. Items and component
d. Only component
And why? It'd great if share with me any info (links, your thoughts, official docs).
Thank u!
The code in the <div id=app> (e.g.<p> <ul>) is not DOM it is HTML markup that the browser then parses and then renders as its DOM. Since there is existing html code in the #app element it will be included by Vue as part of its template and thus be part of the Virtual DOM. Though technically since the <p> element never has any Vue operations performed on it, it is ignored.
If you have Vue Devtools extension installed in your browser you can see the representation of the Virtual DOM in the components view. <ROOT> will be your #app div of course, and then you will only see any <components> there, not the <p> element or even the <ul><li> elements.
It is a good practice to never have any html markup at all or otherwise component tags in your root element. Simply<div id='app'></div> then have all the other elements rendered exclusively by Vue through the Virtual DOM. This is achieved simply through the render function.
new Vue({
el: '#app',
components: {
TopLevelComponent
},
render: function(createElement) {
return createElement(TopLevelComponent);
}
})
https://v2.vuejs.org/v2/guide/render-function.html#The-Virtual-DOM
I'm currently rendering all the html server-side and I'm trying to get vue to use this html as the $el for the following two components. As far as I understand from the lifecycle diagram, this should work.
There is a parent Vue instance (which binds to #main) and contains a child component (via the <mod-sidebar> element in the html).
Now I get the following error message:
[Vue warn]: v-on:click="gotoSlide" expects a function value, got undefined
And the v-text directive isn't working either, even though the data is clearly defined.
Thus, the issue seems to be, that the parent instance already compiles all of the directives in the child component as its own (e.g. v-text in the child component is already compiled by the parent, before the child is initialized it seems).
Is there any way to prevent that from happening, so that the directives within the custom child tag <mod-sidebar> are only compiled by the child element?
Vue.component(`mod-sidebar`, {
ready: function() {
// initialize slider
},
data: function() {
return {
name: 'slider',
};
},
methods: {
gotoSlide: function(event) {
return false;
},
},
replace: false
});
new Vue({
el: '#main'
});
<main class="skel-main" id="main" role="main"> <!-- parent -->
<mod-sidebar class="sidebar"> <!-- child -->
<div class="sidebar--slider">
<div class="sidebar--slider-item">
<a class="sidebar--slider-link" href="#" #click="gotoSlide">
<span class="sidebar--slider-text" v-text="name"></span>
</a>
</div>
</div>
</mod-sidebar>
</main>
http://jsfiddle.net/gtmmeak9/27/
You are missing inline-template
<mod-sidebar class="sidebar" inline-template>
I'm trying to build a simple memory card game in Meteor and I can't seem to create a grid of my cards to populate in html. I'm just trying to get a 4x4 grid for right now.
Here is the javascript:
Template.body.helpers({
cards: function() {
var allCards = Deck.find({}).fetch();
var chunks = [];
while (allCards.length > 0) {
chunks.push(allCards.slice(0, 4));
allCards = allCards.slice(4);
}
return chunks;
},
});
And here is the HTML:
<body>
<div class="container">
<div name="gameBoard">
{{#each cards}}
{{> cardsRow}}
{{/each}}
</div>
</div>
</body>
<template name="cardsRow">
<div class="row">
{{#each row}}
{{> card}}
{{/each}}
</div>
</template>
<template name="card">
<span class="text">{{text}}</span>
</template>
Right now I just have simple text entries in the db to see what I'm doing before I pull in images. I've tried console logging from my JS and I believe I'm creating the array of spliced rows correctly so I think the problem may be in the way I have arranged the markdown.
I tried pulling the each cards loop into a template as well instead of inside the body but I'm not sure how to render templates on demand. This is potentially an option since I have a new game button event listener that could call a render. I'm just not sure how to explicitly render in Meteor.
I tried following this previous post but I couldn't get it to work:
How do I populate a bootstrap grid system using handlebars for each command in Meteor.js?
Thoughts? I can provide more of my code base if needed.
Neither row in {{#each row}} in the "cardsRow" template, nor {{text}} in the "cards" template, refer the way that you hope they do. You need to define row and text as template helpers for each of these templates. In a template helper, this refers to the data object associated with the template.
In your case, when you iterate through #each cards in "container", this is passed into the "cardsRow" template as one of the chunks defined in your cards helper function. The helpers below should illustrate this, and are sufficient for your example.
Template.cardsRow.helpers({
row: function() {
console.log(this); // a chunk of cards
return this;
}
});
Template.card.helpers({
text: function() {
console.log(this); // a number in a chunk
return this;
}
});
I'm new to Ember and I'm stuck trying to render views.
The application in context is not a single page application but so far Ember has been playing nice until I started dealing with views.
I'm trying to render more than one view on a single page (which is like a dashboard and has tons of static HTML and a few containers, one for each view).
Sample HTML:
Let's say I would like to render two lists, one inside the "left-container" div and the other inside the right container.
<div class="static-header></div>
<!-- more static content goes here -->
<div id="left-container"></div>
<!-- more static content goes here -->
<div id="right-container"></div>
...
I've tried creating different views and inserting them using the appendTo method (which is described in the Defining a View section of Ember guides) but it throws the error:
Container was not found when looking up a views template. This is most likely due to manually instantiating an Ember.View. See: http://git.io/EKPpnA
and I couldn't find my way using the link that it points to.
Ember code:
var App = Ember.Application.create({});
App.HomeViewLeft = Ember.View.extend({
templateName: 'home-left',
});
var view = App.HomeViewLeft.create();
view.appendTo('#left-container');
I have also tried using a ContainerView as described in Ember api docs:
App.HomeViewLeft = Ember.View.extend({
templateName: 'home-left',
});
var containerView = Ember.ContainerView.create({
classNames: ['container-view'],
});
containerView.pushObject(App.HomeViewLeft.create());
containerView.appendTo('left-container');
But I get the same error.
How should I render each view inside the #left-container and #right-container respectively?
Thanks in advance.
Template:
<div id="here"></div>
<script type="text/x-handlebars" data-template-name="components/my-foo">
{{text}}
</script>
JS:
App = Ember.Application.create({});
Ember.Application.initializer({
name: 'stand-alone-components',
initialize: function(container, application) {
App.MyFooComponent.create({text: 'Hello World', container: container}).appendTo('#here');
}
});
App.MyFooComponent = Ember.Component.extend({
init: function() {
this._super();
this.set('layout', Ember.TEMPLATES['components/my-foo']);
}
});
http://emberjs.jsbin.com/nekowidera/1/edit?html,js,output
It looks like you are trying to make views behave like partials
Move anything static into a partial and include it in your main template like so:
{{partial "header"}}
The lists you want to render can be turned into a component (if the only thing that changes in them is the data).
So you end up with a single view that contains partials and components:
<div class="static-header>{{partial "header"}}</div>
{{partial "static-content"}}
<div id="left-container">{{list-component data=firstList}}</div>
{{partial "static-content"}}
<div id="right-container">{{list-component data=secondList}}</div>
...
EDIT my humble mockup of what I want to implement
I have defined such a view:
define(['jquery', 'underscore', 'backbone', 'text!_templates/gv_container.html', 'bootstrap/bootstrap-tab'],
function($, _, Backbone, htmlTemplate, tab) {
var GridViewContainer = Backbone.View.extend({
id: 'tab-panel',
template: _.template(htmlTemplate),
events: { 'click ul.nav-tabs a': 'tabClicked' },
tabClicked: function(e) {
e.preventDefault();
$(e.target).tab('show');
},
render: function() {
this.$el.append(this.template);
return this;
}
});
return GridViewContainer;
}); // define(...)
The view's template looks like this:
<ul class="nav nav-tabs">
<li class="active">Products</li>
<!-- other tabs -->
</ul>
<div class="tab-content">
<div class="tab-pane active" id="products">
<!-- table with rows like { title, few more properties, edit|remove links } -->
</div>
<!-- other panes ... -->
</div>
Initially I thought I might use it (as common template for all collections at once).
Goal
I have three collections: products, categories, orders. And I want to insert each of them as tables on separate tab-panes. As for passing models to GridViewContainer I think I can wrap them in a composite model and simply pass the latter one as a part of options object to GridViewContainer view. What's next?
Product, Order, Category models have different properties, have different edit forms and probably event handling. This brings specificity. Should I use [EntityName]ListView per collection and then append it to GridViewContainer view?
ASIDE
I use jquery.js, underscore.js, backbone.js, require.js, and ASP.NET MVC 4 on server side.
I don't use Marionette.js
I would recommend to create a view for each model.
You may create a abstract wich holds the shared stuff like table creation.
Each view extends your abstract instead of Backbone.View.
var myAbstract = Backbone.View.extend({...})
var productsView = myAbstract.extend({...})
Then create a view, handling the wrapper (Tabs).
Example: http://jsfiddle.net/DC7rN/