Create polymer templatizer without registering custom element? - polymer-1.0

I can create a custom component having Polymer.Templatizer behavior, and use that component to stamp out templates:
<dom-module id="my-template">
...
<script>
Polymer({
is: 'my-template',
behaviors: [Polymer.Templatizer],
...
})
...
...
var htmlTemplate = this.querySelector('template');
var myTemplate = document.createElement('my-template');
myTemplate.templatize(htmlTemplate);
var stamped = myTemplate.stamp({prop1:val1});
var target = ...;
target.appendChild(stamped.root);
Is it possible to create a stampable component without needing that custom element?

Related

Common-Js and Material-UI Icons. Understanding why Icon won't load. (React Js)

I'm attempting to run the following code:
"use strict";
var React = require('react');
var Router = require('react-router');
var Link = Router.Link;
var Material = require('material-ui');
var ThemeManager = new Material.Styles.ThemeManager();
var Colors = Material.Styles.Colors;
var dropdown = Material.Icons.NavigationArrowDropDown; //This icon cannot be found
var Home = React.createClass({
childContextTypes: {
muiTheme: React.PropTypes.object
},
getChildContext: function () {
return {
muiTheme: ThemeManager.getCurrentTheme()
};
},
componentWillMount: function () {
ThemeManager.setPalette({
accent1Color: Colors.cyan500
});
},
render: function () {
return (
<div>
<Material.AppBar title="Test" showMenuIconButton={false}>
</Material.AppBar>
<Material.List>
<Material.ListItem primaryText={"Queue"} leftIcon={<Material.Icons.NavigationChevronLeft/>} />
<Material.ListItem primaryText={"Log"} leftIcon={<Material.Icons.NavigationArrowDropDown/>} />
<Material.ListItem primaryText={"Settings"} />
</Material.List>
<Material.Paper>
<span>This is some text</span>
<Material.RaisedButton label="Super Secret Password" primary={true}/>
</Material.Paper>
</div>
);
}
});
module.exports = Home;
I've included the necessary packages and the code runs fine if I don't include
Material.Icons.NavigationArrowDropDown;
I've navigated to material-ui (0.11.1) and the file does exist there as an export in the following path:
lib > svg-icons > Navigation > Arrow_drop_down.js and the source code is as follows:
'use strict';
var React = require('react/addons');
var PureRenderMixin = React.addons.PureRenderMixin;
var SvgIcon = require('../../svg-icon');
var NavigationArrowDropDown = React.createClass({
displayName: 'NavigationArrowDropDown',
mixins: [PureRenderMixin],
render: function render() {
return React.createElement(
SvgIcon,
this.props,
React.createElement('path', { d: 'M7 10l5 5 5-5z' })
);
}
});
module.exports = NavigationArrowDropDown;
However, when compiling and running the application it cannot find the item and complains it does not exist, yet the other item
Material.Icons.NavigationChevronLeft
Gets found without issue. This file (with the exclusion of my router and app.js) are my entire project.
Since both files exist in the same folder, I cannot understand why the one reference is found and the other isn't?
The error occurs at runtime and jsLint doesnt pick it up. Additionally, when removing the listItem icon my page renders correctly. The problem appears to be tied directly to this component.
Additional Note: I have removed the var dropdown, it was there merely to demonstrate how the export is not being found from Material UI.
tl;dr : Material UI Icon class in the same folder as another Icon class is not being picked up. Why?
As you can see in src/index.js, NavigationArrowDropDown isn't being set on Material.Icons, while NavigationChevronLeft is. The component is used in other places, but is never publicly exposed through material-ui's main export.
However, you can still require it like you would any other file:
var NavigationArrowDropDown = require('material-ui/lib/svg-icons/navigation/arrow-drop-down');
Looking at the README, it looks like this is the recommended way to reach single components.

Marionette layout view -- why is a template necessary

In spite of reading the marionette docs several times over, I am still not able to fully comprehend some aspects of it correctly.
I am creating a layout view 'AppLayout' as below:
var AppLayoutView = Marionette.LayoutView.extend({
regions: {
headerRegion: "#ecp_header",
bodyRegion: "#ecp_layout_region"
},
...
The html snippet for my app is having the two dom nodes for above defined regions:
<div id="ecp_header"></div>
<div class="container" id="ecp_layout_region">
<div class="row" id="ecp_body">
...
in app.js, my calling code is like this..
ECPApp.on('start', function() {
require(['controller_cp', 'header_view'], function(ControllerCP, HeaderView) {
console.log("On start event executing...");
// create a event aggregator vent object and attach to app.
ECPApp.vent = new Backbone.Wreqr.EventAggregator();
var appLayoutView = new AppLayoutView();
appLayoutView.render();
//appLayoutView.showLayout();
//$('div.toolbar > ul > li:first > a').tab('show');
if (Backbone.history) Backbone.history.start();
});
This gives me error Cannot render the template since it is null or undefined.
I thought that the default render() behavior of layout always looks for a template, so I rolled out my own version of render, as below:
render: function() {
var $self = this;
/* if no session exists, show welcome page */
var promise = ECPApp.request('entities:session');
promise.done(function(data) {
if (data.result==0) {
console.log('Valid session exists. Showing home page...!');
$self.showHome();
} else {
console.log('No session exists. Showing welcome page...!');
$self.showWelcome();
}
}).fail(function(status) {
console.log('No session exists. Showing welcome page...!');
$self.showWelcome();
});
return $self;
},
showWelcome: function() {
var self = this;
require(['header_view', 'welcome_view'],
function(HeaderView, WelcomeView) {
var headerView = new HeaderView();
var welcomeView = new WelcomeView();
self.bodyRegion.show(welcomeView);
});
}
This time, I get another error saying, An "el" #ecp_layout_region must exist in DOM. However I am sure that the element is existing in the DOM, as I can see it by checking in the debug console window. Running $('#ecp_layout_region') shows a valid element.
Marionette layout view is pretty confusing. Going forward I need multiple nested views. I am stuck here.
How is your template located? Is your template wrapped by <script type = “text/template”> tag?
It may look like this:
Inside your html, in head section:
<script type = “text/template” id="yourLayout">
<div id="ecp_header"></div>
<div class="container" id="ecp_layout_region">...</div>
</script>
And in Layout definition:
var AppLayoutView = Marionette.LayoutView.extend({
template: '#yourLayout'
...
});

my backbone sample is not working and giving with this.el.html is not a function

I am new bee to backbone and i am trying with a below sample
http://jsfiddle.net/naveencgr/L3orucjm/
While loading i am getting this.el.html is not a function, let me know what is the cause of it.
HTML:
<div id="container"></div>
<script type="text/template" id="template">
<input type="text" name="Name" id="Name"></input>
<input type="button" name="Add" id="Add" value="Add"></input>
<div id="list"></div>
</script>
JavaScript:
NameModel = Backbone.Model.extend({
});
var nameModel = new NameModel();
nameModel.set('name','test');
alert(nameModel.get('name'));
NameView = Backbone.View.extend({
tagName:"li",
render: function(){
var template=_.template("<%=name%>", nameModel.toJSON());
return template;
}
});
var nameView = new NameView();
NameViewList = Backbone.View.extend({
initialize: function(){
this.render();
},
render: function(){
var template = _.template($("#template").html(), {});
this.el.html(template);
},
events : {
"click input#Add" :"addName",
},
addName : function() {
var name = $("#Name").val();
if (name.trim().length > 0) {
//nameModel.set("name", name);
$("#list").append(nameView.render());
}
}
});
var nameViewList = new NameViewList({el : $("div#container")});
You have numerous errors in your code. You know about one of them but not the others.
The one you know about is:
this.el.html(template);
A view's el is just a DOM node and that doesn't have an html function. The html function you're trying to use is part of jQuery so you'd want to call it on this.$el (which is just a cached version of $(this.el)):
this.$el.html(template);
Other problems:
Your fiddle is missing vars all over the place; don't say:
NameModel = ...
say:
var NameModel
to avoid accidental globals.
Your NameView is strange. It has tagName: 'li' so presumably it should be creating list elements but the render doesn't do anything with the view's el, it just returns a string of HTML that ends up inside a <div>. That <div> should be a <ul>:
<ul id="list"></ul>
A render function generally populates the view's el and, to allow chaining, returns this:
render: function() {
var template = _.template('<%= name %>');
this.$el.html(template(nameModel.toJSON()));
return this;
}
You are using Underscore's _.template incorrectly. You used to be able to say:
var h = _.template(template_source, data);
to compile the template and fill it in in one step but as of Underscore 1.7, the second argument to _.template is an options object. Now you need to compile and fill in the template in separate steps:
var t = _.template(template_source);
var h = t(data);
You'll see this change in the render above.
The way you're using your NameView is strange. Apparently you are trying to use one NameView to handle multiple names, this would work with your strange NameView#render implementation but it will fall apart once NameView has anything to do or once NameView is updated (as above) to be more conventional. You should create one NameView for each name you're displaying and each NameView should have its own NameModel as its model property. This would make NameView#render look like:
render: function() {
var template = _.template('<%= name %>');
this.$el.html(template(this.model.toJSON()));
return this;
}
and NameViewList#addName would look like:
addName: function() {
var name = this.$("#Name").val();
if(name.trim().length > 0) {
var nameView = new NameView({
model: new NameModel({ name: name })
});
this.$('#list').append(nameView.render().el);
}
}
You'll note that we're using NameView#render's new return value, this x.append(v.render().el) pattern is quite common and idiomatic in Backbone so it is a good practice. You should also notice that the search for #list is now limited to the view's el by using the view's this.$ function; this.$('#list') is equivalent to this.$el.find('#list') and doing things this way helps you keep your views self-contained.
In real life you'd probably put your new NameModel({ name: name }) instances in a collection somewhere and events on that collection would trigger the creation of new NameViews.
Applying all that to your fiddle gives you this functional and more idiomatic version:
http://jsfiddle.net/ambiguous/8x0ma9qo/

Can I pass a global var to an underscore view?

I wonder if possible to do pass a global variable when rendering a template. Basically I get this variable each time I call a controller, so it looks like this:
window.myVar = 0;
//this var will change in a given moment when I make a request to the server.. so:
//into the render method of my view, I have something like this:
var template = _.template($("#myTemplate").html(), { varIwantToPass : myVar } );
this.$el.html(template);
this way I can access it into the template with something like this:
<%= varIwantToPass.get('myVar') %>
if this possible?; and also, each time a render the view, this code will excute again and update the value?
Yes, except you template has to be
<%= varIwantToPass %>
and pass variable as window.myVar so you would not accidentally replace it
And yes it will update after each render if you pass the variable each time
Working example:
Html:
<script id="myTemplate" type="text/html">
<%= varIwantToPass%>
</script>
<div></div>
JS:
window.myVar = 'a';
var templateHtml = $("#myTemplate").html()
var render = function () {
var template = _.template(templateHtml, { varIwantToPass : window.myVar } );
return template;
}
$('div').html(render());
window.myVar = 'b'; //change variable
setTimeout(function() {
$('div').html(render());
}, 1000)
http://jsfiddle.net/omynhL1d/
However I would advocate on not using global variable and instead saving it somewhere in your backbone view or even better a model and then render by listening on that model change event

Add custom property to every item of a collection

I'm trying to add a custom property to every item of a collection, but it doesn't show up in the template.
I have a lot of quotations which have a client_id. Now I want to fetch the client by the client_id and add it to the collection entry. In general, it works when inspecting the populated object with console.log, but it doesn't show up in the template.
That's how I tried it:
sprocket.QuotationsView = Backbone.View.extend({
id: 'content-inner',
initialize: function(options) {
// instantiate Collection
this.collection = new Quotations();
// compile Handlebars template
this.tpl = Handlebars.compile(this.template);
},
render: function() {
var self = this;
var obj = this.el;
// get quotations and set data to handlebars template
$.when(this.collection.fetch()).then(function(quotations) {
$.each(quotations, function(i, quotation) {
var loadContact = new Contact({id: quotation.contact_id}).fetch();
$.when(loadContact).then(function(contact) {
quotations[i]['contact'] = contact;
});
});
$(obj).html(self.tpl(quotations));
// if response is empty (no quotations in database), set empty template
}, function() {
$(obj).html(self.tpl);
});
return this;
}
});
My template looks like this:
<div>
{{#if .}}
{{#each .}}
{{number}} <!-- this works -->
{{contact}} <!-- this doesn't work -->
{{contact.name}} <!-- this doesn't work too -->
{{/each}}
{{/if}}
</div>
That is because the callback that actually changes the data inside the Quotation.attribute.contact (ie. your quotations[i]['contact'] = contact; line) is being executed after fetching the Contact which happens to be after the template is being rendered.
$.each(quotations, function(i, quotation) {
var loadContact = new Contact({id: quotation.contact_id}).fetch();
// This is the callback that is being executed after the backend responds
$.when(loadContact).then(function(contact) {
quotations[i]['contact'] = contact;
});
});
// This is the template rendering which is before the server has time to respond
$(obj).html(self.tpl(quotations));
Render instead the template after all Contacts are being fetched and added to Quotations.
A quick way to solve this:
Make the loop which load all the Contacts inside a function that contains a callback.
Call the callback after all Contacts have loaded.
The callback should render the template.
This is a personal opinion and in no way an answer to this question: I don't like the data logic with the backend and the view rendering and logic in the same class. I use Backbone.Marionette to split the View and the Controller in two different entities loosely coupled by events. Only the Controller is aware of the View and only the View is aware of the DOM.

Resources