Show Backbone Collection element in a Marionette region - layout

I have a Backbone application running and working properly with requirejs. Now, I'm trying to make a migration to Marionette in order to have the code better organized.
I just want to show a model from a collection in a region, with two buttons in another region. I want to go to the next or previous model from that collection. And change its view on the model region.
But I don't know how to iterate over the collection and send its model to the view.
jsfiddle with some simplified code with this situation.
html:
<div class="player"></div>
<script id="layout-template" type="text/template">
<div class="modelView"></div>
<div class="controls"></div>
</script>
<script id="model-region" type="text/template">
<%=name%>
</script>
<script id="control-region" type="text/template">
<button id="prev">Prev</button>
<button id="next">Next</button>
</script>

If I understand your question, you are trying to coordinate events between two views on the same layout. In this case I would recommend setting up a Controller.
Then you can register view triggers for on your controls view:
ControlsView = Backbone.Marionette.ItemView.extend({
// ...
triggers: {
"click #previous": "control:previous",
"click #next": "control:next"
}
});
An then in your controller you would instantiate your views and setup a handler for the controlView triggers to update the modelView.
var Router = Marionette.AppRouter.extend({
appRoutes: {
"/": "start",
"/:index" : "showModel"
},
});
var Controller = Marionette.Controller.extend({
initialize: function(){
var self = this;
this.controlsView = new ControlsView();
this.modelView = new MainView();
this.myCollection = new MyCollection();
this.myIndex = 0;
this.myCollection.fetch().then(function(){
self.myIndex = 0;
});
this._registerTriggers();
},
start: function(){
this.controlLayout.show(this.controlView);
this.showModel();
},
showModel: function(index){
index = index || this.index;
this.modelView.model = myCollection.at(this.index);
this.modelLayout.show(this.modelView);
},
showNext: function(){
var max = this.myCollection.models.length;
this.index = max ? 1 : this.index + 1;
this.showModel();
},
showPrevious: function(){
var max = this.myCollection.models.length;
this.index = 0 ? max : this.index - 1;
this.showModel();
},
_registerTriggers: function(){
this.controlsView.on("control:next", this.showNext());
this.controlsView.on("control:previous", this.showPrevious());
}
}
var controller = new Controller();
var router = new Router({
controller: Mod.controller
});
controller.start();
Using this approach allows you to decouple your views and collection. This will make your code reusable (using the controls view in a different context as an example).

You are looking for CollectionView or CompositeView.
The CollectionView will loop through all of the models in the
specified collection, render each of them using a specified itemView,
then append the results of the item view's el to the collection view's
el.
A CompositeView extends from CollectionView to be used as a
composite view for scenarios where it should represent both a
branch and leaf in a tree structure, or for scenarios where a
collection needs to be rendered within a wrapper template.

Related

get data from nodejs to frontend js for google maps but stuck in retrieving it looking for ways to solve it

My NodeJS GET route:
router.get('/stores', function (req, res, next) {
errorMsg = req.flash('error')[0];
successMsg = req.flash('success')[0];
Product.find(function (err, products) {
// console.log(products)
res.render('admin/stores', {
layout: 'admin-map.hbs',
stores: products,
errorMsg: errorMsg,
GOOGLE_APIKEY: process.env.GOOGLE_APIKEY,
noErrors: 1
});
});
The route /stores returns json data which holds latitude and longitude and I want it in my script tag of map.html with loop, to render the pins on the map. Below, the script:
<!DOCTYPE html>
<html>
<head>
<script src = "https://maps.googleapis.com/maps/api/js"></script>
<script>
function loadMap() {
alert(this.stores);
var mapOptions = {
center:new google.maps.LatLng(17.433053, 78.412172),
zoom:5
}
var map = new google.maps.Map(document.getElementById("sample"),mapOptions);
var marker = new google.maps.Marker({
position: new google.maps.LatLng(17.433053, 78.412172),
map: map,
draggable:true,
icon:'/scripts/img/logo-footer.png'
});
marker.setMap(map);
var infowindow = new google.maps.InfoWindow({ });
infowindow.open(map,marker);
}
</script>
<!-- ... -->
</head>
</html>
How can I do it?
It seems you need to follow two steps
1. Pass data from hbs to script
Using triple brackets syntax
<script>
let stores = {{{ stores }}}; // the triple brackets
console.log('Data : ', stores);
function loadMap() {
...
Check if data is being printed in console? If yes your data is available in the front-end script and you can
2. Loop through it
...
for (let i = 0; i < stores.length; i++) {
// the JS loop instead of hbs one, because we are on front-end
var marker = new google.maps.Marker({
position: new google.maps.LatLng(stores[i].lat, stores[i].lng), // whatever applies
map: map,
draggable:true,
icon:'/scripts/img/logo-footer.png'
});
}
And donot need to call setMap(), you have already set the map in map: map above
the answer is inside fronted script i can have an object declared globally and this double flower brackets works fine with them
<script>
function loadMap() {
alert(this.stores);
var mapOptions = {
center:new google.maps.LatLng(17.433053, 78.412172),
zoom:5
}
var map = new google.maps.Map(document.getElementById("sample"),mapOptions);
{{#each stores}}
var marker = new google.maps.Marker({
position: new google.maps.LatLng(17.433053, 78.412172),
map: map,
draggable:true,
icon:'/scripts/img/logo-footer.png'
});
{{/each}}
marker.setMap(map);
var infowindow = new google.maps.InfoWindow({ });
infowindow.open(map,marker);
}
</script>

Dynamically publish attributes in Polymer

Is it possible to publish new Polymer component attributes at runtime?
<polymer-element name="dynamic-attributes">
<template></template>
<script>
Polymer('dynamic-attributes', {
ready: function(){
var attributes = new Thing().getAttributes();
this.publishAttributes(attributes); // imaginary method
},
});
</script>
</polymer-element>
More info...
I'm writing a component lib that wraps Seriously.js.
Here is an example of the ideal implementation.
<seriously-graph linear>
<seriously-source>
<img src="images/pencils.jpg">
</seriously-source>
<seriously-effect type="pixelate" pixelSize="{{pixelSize}}"></seriously-effect>
<seriously-effect type="blur" amount=".5"></seriously-effect>
<seriously-target width="411" height="425"></seriously-target>
</seriously-graph>
I have a work-around that uses MutationObserver to detect attribute changes. It works, but then I have to find a solution for serialization and make property getters/setters. It feels like I'm re-inventing the wheel (Polymer), and was hoping there was a built-in method of doing this.
Here's a Gist that does what you're looking for. Basically, it dynamically creates a concrete <polymer-element> for an effect the first time it's used, and then swaps the generic instance with a dynamically created concrete one, copying any binding declarations in the process. The source would be:
<seriously-effect type="blur" amount="{{someAmount}}"></seriously-effect>
But with Inspect Element, you'd see:
<seriously-effect-blur amount="{{someAmount}}"></seriously-effect-blur>
Yes, it is possible by assigning an attributes object to publish node. See https://www.polymer-project.org/docs/polymer/polymer.html#attributes for details.
Your method must return an object of attributes:
Polymer('dynamic-attributes', {
publish: (new Thing()).getAttributes() || {}
});
Maybe you could make a global array and use it as attribute?
<polymer-element name="app-globals" attributes="values">
<script>
(function() {
var values = {};
Polymer({
ready: function() {
this.values = values;
for (var i = 0; i < this.attributes.length; ++i) {
var attr = this.attributes[i];
values[attr.nodeName] = attr.value;
}
}
});
})();
</script>
</polymer-element>
<app-globals id='myDynamicFunctions' someFunction someOtherFunction></app-globals>
<script>
//then define it
document.$.myDynamicFunctions.values.someFunction = function(){...}
</script>
I am not quite sure what you are looking for, this might give you a hint...

multiple d3 visualizations in one HTML document

I want to have multiple d3 visualizations in one document. My idea is to store some configuration such as url of the RESTful service along with the SVG tag attributes. On document load d3 would read in the attributes and load the data from the server and create the visualization. In this way I could edit / rearrange the document while keeping the visualizations intact.
My question is whether there is already a standardized way (best practice) of doing this? Or is there some plugin or something I could use?
change: I realized that I should mention that I want to create different documents with the same code. Consequently the document defines the content of the visualization (not the code).
A sample document with SVG attributes:
...
<head>
...
<svg width="200"... src="localhost:8000/rest/host1/cpu" type="os.line">...</svg>
<svg width="200"... src="localhost:8000/rest/host1/memory" type="os.bar">...</svg>
in the end I decided to move the configuration into paragraphs (integrates well with redactor):
<p class="plot" src="localhost:8000/rest/host1/cpu" type="os.line">...</p>
<p class="plot" src="localhost:8000/rest/host1/memory" type="os.bar">...</p>
... here is my code:
define(['lib/d3.v3.min'], function(ignore) {
var visualise = function(plugins) {
var _host = function(src) {
return src.split("/")[4];
};
var _plot = function(type) {
var parts = type.split(".", 2);
return plugins[parts[0]][parts[1]];
};
d3.selectAll("p.plot")
.each(function() {
var div = d3.select(this);
var plot = _plot(div.attr("type"));
var url = div.attr("src");
var host = _host(url);
d3.csv(url, function(data) {
div.call(plot, data, host);
});
});
};
return {visualise: visualise};
});
comments, improvements are more than welcome.
Why not just append each visualization to a different div?

Does Knockout.mapping make ALL nested objects observable?

I am trying to map all possible nested objects of a JSON object so that each and every one is becomes an observable. I was under the impression that the use of ko.mapping.fromJS would result in all objects and their objects becoming observable. However, I am not seeing that happen.
If you look at the JSFiddle and code below you will see that the span initially displays the value "Test". My intention is for the button click to update the viewModel with the contents of stuff2, which should change the span's value to "Test2". However, the button click does not update anything.
http://jsfiddle.net/Eves/L5sgW/38/
HTML:
<p> <span>Name:</span>
<span data-bind="text: IntroData.Name"></span>
<button id="update" data-bind="click: Update">Update!</button>
</p>
JS:
var ViewModel = function (data) {
var me = this;
ko.mapping.fromJS(data, {}, me);
me.Update = function () {
ko.mapping.fromJS(stuff2, {}, windows.viewModel);
};
return me;
};
var stuff = {
IntroData: {
Name: 'Test'
}
};
var stuff2 = {
IntroData: {
Name: 'Test2'
}
};
window.viewModel = ko.mapping.fromJS(new ViewModel(stuff));
ko.applyBindings(window.viewModel);
Is it just that I have to make use of mapping options to have the nested objects be made observable? If so, what if the JSON object is so vast and complex (this one obviously isn't)? Can some recursive functionality be used to loop through each object's nested objects to make them all observable?
Modifying the Update function as below will work.
me.Update = function () {
ko.mapping.fromJS(stuff2, {}, windows.viewModel);
};

Backbone events applied to an el that is an svg element

I have an SVG canvas with text that I would like to respond to clicks. The code below isn't getting the job done:
var MyView = Backbone.View.extend({
tagName : "text",
events : {
"click" : "clickhandler"
},
initialize : function() {
this.centerX = this.options.centerX;
this.centerY = this.options.centerY;
this.svg = this.options.svg;
this.tagText = this.model.get("tag_name");
this.render();
},
clickhandler : function(event) {
console.log("I was clicked!"); //This is not firing on click
},
render : function() {
this.el = this.svg.text(this.centerX, this.centerY, this.tagText, {});
return this;
}
});
This is being called in the render function of another view as such :
container.svg({
onLoad : function(svg) {
for ( var i = 1; i < that.relatedTags.length; i++) {
tagView = new MyView({
model : this.relatedTags.at(i),
centerX : 100,
centerY : 200,
svg : svg
});
}
container.append(tagView);
}
});
It shows up just fine and if I throw this in at the end of the for loop :
$(tagView.el).click(function() {
alert("xx");
});
Then the clicking works but I need to access the Backbone Model associated with the view so I'd much prefer the backbone event to a straight JQuery event.
The problem here is, that you set the element of the view in the render method. But backbone tries to add the events on initialization. So when backbone tries to add the events there is no element in you case. So either you have to start your view with your svg text, or you add the events by hand in your render method.
Maybe you can add the events on the svg itself and jquery is clever enough to handle the delegation. But I'm not sure in this case.

Resources