I'm not getting any console logs/errors, but the view I'm trying to create is inserting the DIV without the usual ID:
<div id="undefined" view="" class="myclass"></div>
Without any errors I'm not sure how to fix this...
view coffee:
class App.SidebarView extends Mozart.View
templateName: 'app/templates/sidebar_view'
classHtml: 'sidebar-content'
init: ->
# stuff to come
view hbs just has some placeholder text.
You need to call super at the beginning of the init function.
init: ->
super
#stuff to come
Related
I have been following https://developer.shopware.com/docs/guides/plugins/plugins/storefront/add-custom-javascript to create a javascript plugin. But, I am struggling with adding options through twig template. I have something like the following:
Twig file : product.html.twig:
{% set contentModalOptions = {
cmsContentId: "some-hash",
navigationUrl: path('frontend.cms.page'),
failSafeRedirectUrl: '/some-failsafe-url/'
} %}
<div>
<a target="_self" href="#" data-content-modal="true" data-content-modal-options="{{ contentModalOptions|json_encode|escape('html_attr') }}">
help text
</a>
</div>
plugin file : custom-plugin.js:
import Plugin from 'src/plugin-system/plugin.class';
export default class ContentModalPlugin extends Plugin {
static options = {
cmsContentId: '',
navigationUrl: '',
failSafeRedirectUrl: ''
};
init() {
console.log(this);
console.log(this.options); // empty values
}
}
Notes:
In the browser, I see that values set using twig as the HTML attribute.
Plugin has been registered and works with the template.
console.log() in the plugin doesn't print any values that are set from twig. It just shows the options object that has been initialized in the plugin.
Any help is appreciated.
I assume you register the plugin like this:
PluginManager.register('ContentModal', ContentModalPlugin, '[data-content-modal]');
Within the constructor of the Plugin class this.options = this._mergeOptions(options); should then called, which in turn parses the data-${dashedPluginName}-options attribute. It should throw an error if it can't parse the json:
`The data attribute "data-${dashedPluginName}-options" could not be parsed to json: ${e.message}`
Are the any errors when you look at the console of your browsers dev tools?
For further debugging you could also try calling super._mergeOptions(this.options); from your init method.
Now, I see what the problem was. When I registered my plugin, I had the following:
PluginManager.register('ContentModalPlugin', ContentModalPlugin, '[data-content-modal]');
So, I tried passing the options with data-content-modal-options attribute through twig but it seems that the resolved plugin name in _mergeOptions() at src/plugin-system/plugin.class takes the plugin name (i.e the string that is the first argument of the register function) and not the attribute definition in the register method.
So, adding a html attribute as data-content-modal-plugin-options based on my class name resolved the problem.
Newbie hacking my way through. I'm trying to pull rows from an array and display them in a screen. I have the array populated and the snippet of code below works:
return (
buildings.map((building) => {
return <div className="col-md-9">
{building.name}
But when I try to use a component in the statement I get an error. This doesn't work:
return (
buildings.map((building) => {
return <div className="col-md-9">
<Building building={building.name}/>
The Building component is here:
import React from 'react'
function Building({building}) {
return (
<div className="row">
<h1>{building.name}</h1>
</div>
)
}
export default Building
The error I get is here:
src/screens/Homescreen.js Line 34:15: 'Building' is not defined
react/jsx-no-undef
I can provide more details if necessary. I think I'm missing something obvious but I'm stuck at the moment so any help is appreciated.
First check to see if you are importing Building component.
secondly, <Building building={building.name}/> you are passing down the name attribute of building and then in the Building component, trying to access that name attribute. This should be changed to <Building building={building}/>. Fix these to see if the problem continues to exist.
I am taking a full stack course via Eduonix.com. It seems that some of the code syntax is deprecated, as I have had to install older versions of multiple things to get through some of the sections. This did not help when I came to the Meteor.js section, so I installed the most recent and did some searching that allowed me to get through the first lesson. I tried the same for the second lesson (when I encountered this error), but have not had any luck with anything I have found.
I get this error when I try to use
todos.find().fetch()
in the browser's console.
Relevant File Structure:
client
--main.html
--main.js
lib
--collections.js
View on github
In the course lesson, the line
import { Todos } from '../lib/collections';
is not present in main.js and the line
export const Todos = new Mongo.Collection('todos');
in collections.js is presented as
Todos = new Mongo.Collection('todos');
I have tried changing
import { Todos } from '../lib/collections';
to
import { Todos } from '/lib/collections';
but it did nothing.
I have also tried just adding
Todos = new Mongo.Collection('todos');
to main.js, but I got an error that said "todos" was already defined (only to get the same undefined error when I tried to run the console command, because somehow it was both already defined and is still undefined).
I have made these changes based on looking up similar issues online before making this post, hoping that it would save me like adding
import './main.html';
to main.js did when I was getting a similar undefined error related to that prior to this error.
main.html
<head>
<title>QuickTodos</title>
</head>
<body>
{{> main}}
</body>
<Template name="main">
<header>
<h1>{{title}}</h1>
</header>
<ul>
{{#each todos}}
{{> todo}}
{{/each}}
</ul>
</template>
<template name="todo">
<li>{{text}}</li>
</Template>
main.js
import { Template } from 'meteor/templating';
import './main.html';
import { Todos } from '../lib/collections';
const todos = [
{text:'Pickup kids from school'},
{text:'Go food shopping'},
{text:'Meeting with boss'}
];
Template.main.helpers({
title(){
return 'QuickTodos';
},
todos(){
return todos;
}
});
collections.js
import {Mongo} from 'meteor/mongo';
export const Todos = new Mongo.Collection('todos');
When I run
todos.find().fetch()
I expect to get an empty array, but instead I get:
VM1905:1
Uncaught ReferenceError: todos is not defined
at <anonymous>:1:1
What am I doing wrong?
The first thing I notice is that the capitalization of these two lines are different, meaning they will point to different variables (thus, todos is not defined):
export const Todos = new Mongo.Collection('todos');
todos.find().fetch()
The second thing, is that because Meteor uses CommonJS modules, any variables you declare will be local to that module, and won't be available on the console.
(there's some exceptions, like the Todos declared without var, let, or const, which will be scoped across the whole application, but still not global or available in the console)
You can force a variable to be global by adding it as a property to the window:
import { Mongo } from 'meteor/mongo';
export const Todos = new Mongo.Collection('todos');
window.Todos = Todos; // here we make it global
And then as long as this file is imported by client/main.js, you can use Todos on the console.
This is fine for small apps and testing stuff, but increases the likelihood of name collisions and makes it harder to understand where code is coming from in larger apps.
If you want to do things the "right" way for a module system, you can access the exports of a module in the console with require:
> Todos = require('/lib/collections').Todos
I am using express-handlebars in my project and have the following problem:
Question
I want to be able to add <script> oder such tags to my overall views head from a partial that is called inside the view.
Example:
The view
{{#layout/master}}
{{#*inline "head-block"}}
<script src="some/source/of/script">
{{/inline}}
...
{{>myPartial}}
{{/layout/master}}
The view is extending another partial (layouts/master) that I use as a layout. It adds its content to that ones head block through the inline partial notation, which works fine
the Partial "myPartial
<script src="another/script/src/bla"></script>
<h1> HELLO </h1>
Now I would like that particular script tag in there to be added to my views head-block. I tried going via #root notation but can only reference context there. Not change anything.
I know I could use jquery or similar to just add the content by referencing the documents head and such. But I wanted to know if this is possible at all via Handlebars.
I do doubt it is in any way. But if you have any ideas or suggestions, please do send them my way! Many thanks!!!
UPDATE
This wont work if you have more than one thing injected into your layout / view. Since this happens when the browser loads the page, it creates some kind of raceconditions where the helpers has to collect the things that have to be injected into the parent file. If its not quick enough, the DOMTree will be built before the helper resolves. So all in all, this solution is NOT what I hoped for. I will research more and try to find a better one...
Here is how I did it. Thanks to Marcel Wasilewski who commented on the post and pointed me to the right thing!
I used the handlebars-extend-block helper. I did not install the package, as it is not compatible with express-handlebars directly (Disclaimer: There is one package that says it is, but it only threw errors for me)
So I just used his helpers that he defines, copied them from the github (I am of course linking to his repo and crediting him!) like so:
var helpers = function() {
// ALL CREDIT FOR THIS CODE GOES TO:
// https://www.npmjs.com/package/handlebars-extend-block
// https://github.com/defunctzombie/handlebars-extend-block
var blocks = Object.create(null);
return {
extend: function (name,context) {
var block = blocks[name];
if (!block) {
block = blocks[name] = [];
}
block.push(context.fn(this));
},
block: function (name) {
var val = (blocks[name] || []).join('\n');
// clear the block
blocks[name] = [];
return val;
}
}
};
module.exports.helpers = helpers;
I then required them into my express handlebars instance like so:
let hbsInstance = exphbs.create({
extname: 'hbs',
helpers: require('../folder/toHelpers/helpersFile').helpers() ,
partialsDir: partialDirs
});
Went into my central layout/master file that`is extended by my view Partial and added this to its <head> section
{{{block 'layout-partial-hook'}}}
(The triple braces are required because the content is HTML. Else handlebars wont recognize that)
Then in the partial itself I added things like so:
{{#extend "layout-partial-hook"}}
<link rel="stylesheet" href="/css/index.css"/>
{{/extend}}
And that did the trick! Thanks!!!
I've set up a general dialog directive with a title and apply/cancel buttons.
The dialog has an isolated scope.
The content of the dialog directive is transcluded and therefor it's scope is a sibling of the dialog scope:
From the Angular js docs:
However isolated scope creates a new problem: if a transcluded DOM is a child of the widget isolated scope then it will not be able to bind to anything. For this reason the transcluded scope is a child of the original scope, before the widget created an isolated scope for its local variables. This makes the transcluded and widget isolated scope siblings.
This presents a new problem for me though. The transcluded DOM should be able to respond the dialog when it is applied. Therefor I'd like to setup an 'applied' property on the dialog and let the transcluded DOM watch it. This is not possible though, because they are siblings!
Where am I going wrong?
I've run into something similar and there are 2 ways (that I know of) to access the transcluded scope directly.
The first is to create the scope yourself inside a compile function and then pass it to the transclude linking function along with a clone linking function:
function compileFn(tElement, tAttrs, transclude) {
return linkFn;
function linkFn(scope, element, attrs) {
scope = scope.$new();
scope.name = attrs.works1;
transclude(scope, function(clone) {
element.find('div').append(clone);
});
};
}
The second is to create a controller and inject the $transclude service which is pre-bound to a new scope. Your clone linking function will receive the new scope as its 2nd parameter:
function Controller($element, $attrs, $transclude) {
$transclude(function(clone, scope) {
scope.name = $attrs.works2;
$element.find('div').append(clone);
});
}
In both cases you'll have to provide a clone linking function to do the transclusion yourself instead of using ngTransclude.
See http://jsfiddle.net/dbinit/wQC7G/6/ for examples of both.
Oke, I think I've found a solution.
I've wrapped the actual dialog in a directive that defines the scope over the dialog.
The content of the dialog is still transcluded in the dialog, but since it will take it's parent scope from the parent of the dialog (!!) and not the dialog itself (transclusion works this way), this will work quite nicely.
Further, I can have the sg-import directive respond when the dialog is applied by using a &property on the dialog. When the dialog is applied, I have it evaluate the sg-apply function in context of the parent scope (the scoping is done automatically, I just have to call the method from the controller's apply() function).
<div sg-import>
<div
sg-dialog title="Import Photographs"
visible="show_import_dialog"
sg-apply="upload()"
>
<div class="drop-zone">
<div sg-photo title="{{ file.name }}">
</div>
<input type="file" multiple />
</div>
</div>
</div>
If you're willing to create a model in the common ancestor to act as a switchboard with $watch targets, you can use pre-existing facilities to having each directive mutate and/or watch that switchboard model. The component's mode of access and the content content's controller have two fery idfferent calls signatures for each scope, and there is a slight "gotcha" for the transcluded case.
Isolated Scope with Bi-Directional Binding
When registering the directive's isolate scope, =attrName" will cause examination of the domainName property named "attrName". Angular will set up bi-directional binding such that a change to the value in either scope's model affect the model in the sibling scope as well.
Example
In controller-parent.js:
module.controller( 'ParentController', function() {
$scope.switchboard = { };
}
In directive-sg-dialogue.js
return {
scope: {
isolatedPeer: "=dialogModel"
};
... in the directive metadata ...
<div ng-controller="ParentController">
<sg-dialog dialog-model="switchboard">
<div ng-controller="ChildController"></div>
</sg-dialog>
</div>
... in some applicaton view template, and ...
$scope.switchboard = { isApplied: false }
... in controller(s) bound to the application view template...
Then you are all set to ...
$scope.$watch( 'switchboard.isApplied', function(newValue, oldValue) { })
... in the common ancestor, and get a callback after ...
isolatedPeer.isApplied = true;
... in the isolated scope.
Prototypical Inheritance
As long as you don't explicitly set $scope.swtichboard in the transcluded child, you can access "switchboard.isApplied" from angular expressions in the transcluded child and have the interpolation engine "find" the value thaat was allocate dand stored by the parent controller in its own scope.
For example, the following callback will get invoked wheneve rthe pair dialogue box is closed:
$scope.$watch( 'switchboard.isApplied', function(newValue, oldValue) { } );
This works because the transcluded child is always given an basic scope, not an isolated scope.
Hope this is helpful!