Access properties from application controller in a rendered template - layout

I have these properties set on my application controller
monthToParse: null,
yearToParse: null,
fullDateToParse: function() {
// set array to month names
var monthNames = [ "January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December" ];
// set a variable to a new date object
var date = new Date();
// set the month to the previous month and return that
date.setMonth(date.getMonth() - 1);
// set the controller properties for use in other parts of the app
this.set('monthToParse', monthNames[date.getMonth()]);
this.set('yearToParse', date.getFullYear());
return this.get('monthToParse') + ' ' + this.get('yearToParse');
}.property('monthToParse', 'yearToParse'),
And they work in my application.hbs.
But I also need to access them in my rendered views layout file.
In index.hbs I'm rendering a bunch of pages like this...
{{render "page-01"}}
{{render "page-02"}}
{{render "page-03"}}
...
and each has a view file that tells it to use a pages layout file like this
import Ember from 'ember';
export default Ember.View.extend({
layoutName: 'page'
});
But in each page.hbs file when I try to reference the applications properties using this technique
In the page controller file
import Ember from 'ember';
export default Ember.Controller.extend({
needs: 'application'
});
In the page.hbs handlebars file
<p>{{controllers.application.fullDateToParse}}</p>
My first thought is that maybe the page.hbs layout file that these pages are using is not properly connected to the controller? Or maybe the nested layout and the way I'm rendering it is off.

I believe by calling {{render 'page-01'}}, you're telling Ember to render the page-01 template with a page-01 controller. You could use the Ember inspector to find out.
You might be able to fix this somehow, but a better solution would be to make your pages into components, and pass in their dependencies explicitly:
{{x-page fullDate=fullDateToParse}}
Then, in your x-page components template, you can just use
{{fullDate}}
Note, depending on what page actually is in your app, there may be a better name for the component than x-page

Related

Persist data sent to a Pug template via render

I'm trying to find out how I can persist the data I pass to my Pug template from the Express render method.
I pass in some JSON data to the res.render() method in Express that renders my view with Pug. On the Pug template, I use that data immediately to populate one of my select elements with drop down values from the JSON data.
What I want to then do is store this data that was passed so I can use it in an event handler function I create for another field.
Basically, I'm passing a table name and the field names for the table, but for each table I have in the JSON data.
So the shape is like [{ tableName: "table name here", fieldNames: ['field1', 'field2', ...] }, ... ]
I have a select field for "choose a table name" and when the user picks a table name, I then want to get the fieldNames for a second select field that allows them to choose a field name to use. So I have an event handler setup on the "choose a table name" field that runs a little event handler I have setup in the pug template. Only problem is the event handler does not have access to the data that was passed to the Pug template originally.
I'm trying to google this but having no luck finding anything, so does anyone know how I can persist data sent via the res.render() method in a pug template for using after the page has been rendered inside an event handler or other functions?
Thank you!
Always be clear what is done at the server (pug) and what is done in client Javascript (browser).
While data passed to pug scripts are meant to be consumed at the server, it is possible to inject, for want of a better word, server data into client side Javascript variables.
The following creates two dropdown lists on the same page using the exact same data passed by Express. One is generated at the server, while the second is created entirely by Javascript running in the browser.
Express code:
app.get("/testdata", (req, res) => {
res.render("testdata", { data: [ 1, 2, 3, 4, 5]});
});
testdata.pug:
html
head
body
p Dropdown list generated at the server:
p
select
each n in data
option(value=n)=n
br
p Dropdown list generated in browser Javascript:
p
select#dropdown
script.
document.body.onload = () => {
const dropdown = document.getElementById("dropdown");
let data = JSON.parse(`!{JSON.stringify(data)}`); // line 18
data.forEach(d => {
const item = document.createElement("option");
item.innerText = d;
dropdown.appendChild(item);
})
}
I have used the same variable name in totally different contexts pointing to different entities. Be careful not to trip. For example, look at line 18:
let data = JSON.parse(`!{JSON.stringify(data)}`); // line 18
The first instance of data is a Javascript variable in the browser.
The second instance of data is a server object passed to the pug script in the render method. Basically any !{expression} instances found in a pug file are evaluated¹ when the view is rendered.
¹ I think the expression is evaluated and its toString method called. If I know it is an array, I could have used:
let data = [!{data}]; // line 18

After passing object to handlebars, how can I access that object in script tag?

I first get the data from sql then pass it into handlebars.
Inside the tag in .handlebars/using view.js, I want to access doctors, but i keep getting[object][object]. I tried json.stringifying it before but still no luck. What is the best way to do this?
umd.matchDocs(val2, function(data) {
console.log(data);
var renderDocs = {
doctors: data
}
res.render("dashboard", renderDocs);
});
After passing object to handlebars, how can I access that object in script tag?
No, not by default. But you can make the data available manually if you want.
Data you pass to handlebars rendering operation is available during the rendering operation only. If you want to be able to access some of that data later in client-side <script> tags, then you can "render" Javascript variables into the <script> tags that contain the desired data.
Remember when rendering data into Javascript variables, you need to render the actual Javascript text (converting to JSON will often create the text for you).
In your specific example, you could do something like this in your rendering code:
umd.matchDocs(val2, function(data) {
console.log(data);
var renderDocs = {
doctors: JSON.stringify(data)
}
res.render("dashboard", renderDocs);
});
And, then in the template:
<script>
var doctors = {{{doctors}}};
</script>
Then, this array of doctors would be available to the Javascript in your page.
In case you haven't seen the triple braces like shown above, that's to tell handlebars to skip any HTML escaping in the data (because this isn't HTML).

How to override template file item-list.html.twig for field_slider_images in Drupal 8?

I want to override the item listing template file core/themes/classy/templates/dataset/item-list.html.twig for listing the fields field_slider_images as well as field_blog_tags respectively of their's multiple values of the field.
I have selected "Unordered List" in the view.
Please do check the attached image.
I have created following files :
item-list--field-blog-tags.html.twig
item-list--field-slider-images.html.twig
But, this is not rendered for the listing of the fields.
When I have created item-list.html.twig then only it will access.
However, both fields have different data to style and I am not able to get the current field name which is loading it's data in item-list.html.twig.
Had a brief look at this and it doesn't seem that 'item-list' to have suggestions, which is quite unfortunate.
In this situation there are two options:
Create your own suggestion which would accomplish exactly what you need.
You'll have to do something like this:
/
/*add new variable to theme with suggestion name*/
function hook_theme_registry_alter(&$theme_registry) {
$theme_registry['item_list']['variables']['suggestion'] = '';
}
//send a value to newly added variable to use it build the suggestion
function hook_ENTITY_TYPE_view(array &$build, $entity, $display, $view_mode) {
//add condition here if field exists or whatever, do the same for other field
$build['field_slider_images']['#suggestion'] = 'field_slider_images';
}
//use newly added variable to build suggestion
function hook_theme_suggestions_THEME_HOOK(array $variables) {//THEME_HOOK=item_list
$suggestions = array();
if(isset($variables['suggestion'])){
$suggestions[] = 'item_list__' . $variables['suggestion'];
}
return $suggestions;
}
Now you should be able to use item-list--field-slider-images.html.twig
Second option is to do what others in core did: use a new theme
function hook_ENTITY_TYPE_view(array &$build, $entity, $display, $view_mode) {
//add condition here if field exists or whatever, do the same for other field
$build['field_slider_images']['#theme'] = array(
'item_list',
'item_list__field_slider_images',
);
}

How to send some data of one template to another template Meteor

I want to share some data from template to another template using Meteor. I have a template i.e allInventory.html on which i am showing some data in table form i added three links there that is. one for view , edit and delete what i want iam getting all the data from backend into one of helper i.e productDetails and i bind an event with view button that will take the data of current user clicked on which product so i have successfully getting the data at my allinventory template but there is another template i.e productDetails on which i want to render or show that data. But stuck with that i have data on allInventory click event but not know how do ishare the same with productDetails template.
Here is my allInventory.js
Template.allInventory.rendered = function() {
Template.allInventory.events({
"click .btn":function (e){
data = $(e.target).attr('data');
Router.go('productDetail', {data: $(e.target).attr('data')}, {query: 'q=s', hash: 'hashFrag'});
console.log("button clicked.."+data);
console.log(data);
}
})
ProductDetails.js
Template.productDetail.rendered = function () {
Template.productDetail.helpers({
productDetails: function() {
return data;
}
});
allInvenrtory.html
<button type="button" data ="{{productInfo}}" class="btn btn-info btn-sm"><i class="fa fa-eye"></i>View</button>
I just simply want to share allInventory template data with productsDetails template.
Any help would be appriciated!
Thanks
I'd recommend avoiding Session for this purpose, since it is a global object, but more importantly, because there are better ways to do it.
You can pass data from the parent templates to the child template using helpers: https://guide.meteor.com/blaze.html#passing-template-content
You can pass data from the child to the parent templates using callbacks https://guide.meteor.com/blaze.html#pass-callbacks
I'd structure this app to have a container (page) template, which will have all the subscriptions and render one of your templates based on the URL.
You can use the Session variable if you want to share data between templates.
You can follow this guide:
http://meteortips.com/first-meteor-tutorial/sessions/
I would put both template in a third, parent template.
ParentTemplate
-SharedInfo
-KidTemplate1
-KidTemplate2
Then having this third template hold the information you want to share across templates.
For that you can use a ReactiveVar, ensuring that change by template1 code on the parent template is visible in template2 as well.
To access the parent template for the kids, you can do something along those lines :
Blaze.TemplateInstance.prototype.parentTemplate = function (levels) {
var view = Blaze.currentView;
if (typeof levels === "undefined") {
levels = 1;
}
while (view) {
if (view.name.substring(0, 9) === "Template." && !(levels--)) {
return view.templateInstance();
}
view = view.parentView;
}
};

How to add an absolute element in a NativeScript page

I want to be able to just place a View component (plugin) into the page through code and have it appear at some X\Y on the page... but I'm a bit stumped.
Any attempt to add via page.content kinda adds it to the layout\render pass so it occupies space.
So this would get injected into "any" page at "any" time, I have no control over the markup this would be used in (know what I mean?) There is no XML for it and unfortunately the answer can't just be wrap everything in an AbsoluteLayout because one can't mandate that on users apps\layouts.
Thoughts, even possible?
Basically the simplest way to do this is to dynamically and be fully cross platform compatible is to create a AbsoluteLayout item in your JavaScript code, and dynamically insert your item and the AL into the page container.
Code would be something like this:
var AbsoluteLayout = require('ui/layouts/absolute-layout').AbsoluteLayout;
var myAL = new AbsoluteLayout();
var myItem = new myPluginItem();
// Set you left, right, top, bottom coords.
myItem.top = x;
// Add our item to the AbsoluteItem
myAL.addChild(myItem);
var frame = require('ui/frame');
var page = frame.topmost().currentPage;
var LayoutBase = require('ui/layouts/layout-base').LayoutBase;
page._eachChildView(function(view) {
if (view instanceof LayoutBase) {
view.addChild(myAL);
return false;
}
return true;
});
However, if you don't want to do this the really simple way; the only other way is to actually go a bit lower level. You can natively access the iOS view controller (page._ios.view) and the android view (page._nativeView), and then manually add it to the view by using things like addView (https://developer.android.com/reference/android/view/ViewManager.html) or addSubview (https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/).
I would like to add you can set the Top and Left properties in TypeScript by importing AbsoluteLayout like so
import {AbsoluteLayout} from 'ui/layouts/absolute-layout';
and then using the exposed functions setLeft or setTop
AbsoluteLayout.setLeft(YourItem, LeftValue);
or
AbsoluteLayout.setTop(YourItem, TopValue);

Resources