I'm trying to style the currently active tab of my web project with the class "active". To target my tab elements I am using
onMount(() => {
const links = document.querySelectorAll(".topnav a");
});
I am then using a reactive statement to style the appropriate element like this
$: {
links.forEach((link) => {
if (link.getAttribute("id") === $page.url.pathname) {
link.classList.add("active");
} else {
link.classList.remove("active");
}
});
}
However, I have no way of sharing the links variable to my reactive statement. I also tried putting document.querySelectorAll inside my reactive statement (not using onMount at all), which worked flawlessly until i reloaded the page. What is the conventional approach to this?
You need to declare the variable outside of onMount so it is in scope of the reactive statement. E.g.
let links = null;
onMount(() => {
links = ...;
);
$: if (links != null) {
links.forEach((link) => {
});
Using document.querySelectorAll is not idiomatic Svelte.
Changing class (or other attributes) use the template syntax:
<a class:active={link.id === $page.url.pathname}>
{link.label}
</a>
If you really need access to the DOM api's Svelte has bind:this or action to get access to specific elements.
Related
The lit-element documentation describes conditional rendering via (condition ? a : b). I was wondering how to use that to render one of multiple pages, f.e. in combination with mwc-tab-bar from Googles material web components.
My current solution is something like this:
render() {
... other stuff ...
${this.selectedPage === 0 ? html`
<div>
...
</div>
` : html``}
${this.selectedPage === 1 ? html`
<div>
...
</div>
` : html``}
... further pages ...
}
I don't like the :html`` part but is that how it's meant to be?
Use more simple code like this.
constructor(){
super();
// don't forget add `prop` and `selectedPage` to `static get properties()`
this.prop = 1;
}
render() {
return this.getPage(this.selectedPage);
}
getPage(num){
switch(num){
default:
case 1:
return html`<div>P${this.prop}<div>`;
case 2:
return html`<div>P2<div>`;
}
}
There are multiple ways of achieving this, your solution is one, but as you mention, it's not the prettiest
One way you could modularize this somewhat is using an object/array and render functions, basically the idea is this:
First, define render functions for each page (this can be on the same file or on different files):
const page0Renderer = (context) => {
return html`<section>${context.someData}</section>`;
};
Then, you could define an object that has a match between the page identifiers and their respective functions, you are using numbers so the sample below uses numbers:
const pageRenderers = {
'0': page0Renderer,
'1': page1Renderer,
'2': page2Renderer,
// etc
};
And in your main render function you could use all these like this:
render() {
return html`
${pageRenderers[`${this.selectedPage}`](this)}
`;
}
This would basically call the render function that matches the selected page and send it a reference to the main web component so that you can access its properties.
Then again, this approach also has its flaws and I wouldn't really recommend it much if you need your child templates to be complex.
In that case, instead of rendering functions you probably would be better off creating other components for each view and that way you could also do some lazy loading and so on.
For that kind of approach, you might want to check out routers like vaadin router which help you both with routing and changing which component gets displayed accordingly
We use Geb to run our Frontend Tests and we have some quite complex pages in our application.
Some of the pages have forms with a lot of different buttons, checkboxes and some multiselects.
I love the feature of geb/groovy that i just have to define the form in the Page Object and then can access all its elements in it.
static content = {
form { $("#form")}
}
But for them to be clickable and to query if they are readonly and more they need to be at least of type FormElement which does not happen with the above method. So I have to mention all these FormElements separately:
static content = {
form { $("#form")}
button1 { $("#button1").module(FormElement)}
button2 { $("#button2").module(FormElement)}
checkbox{ $("#checkbox").module(Checkbox)}
...
}
All those buttons, checkboxes... are already in the form variable, but cannot be clicked or checked if they are selected and so on. It's also not possible to apply the the module afterwards like this:
def "test something"() {
when:
form.button1.module(FormElement).click() //error
then:
...
}
Is there no way to automatically assign each input, checkbox, radiobutton, button,... the correct Module based on their type without the need of doing it by hand?
If someone could also point me in the right direction to understand how this "form { $("#form")}" works, that i can access all sub elements by its name by just suppying the form, that would be nice!
For your example of creating a module based on a form control you need to obtain a navigator for the control and not it's value. It's done by calling a method named the same as the control you're trying to access (it's explained in this section of The Book of Geb):
form.button1().module(FormElement).click()
If you want to automatically create modules based on the element type then you could create a Module for the form and override method missing:
class FormModule extends Module {
Object methodMissing(String name, Object args) {
def result = super.methodMissing(name, args)
if (result instanceof Navigator && result.tag() == "input") {
switch (result.#type) {
case "checkbox":
result = result.module(Checkbox)
break
default:
result = result.module(FormElement)
}
}
result
}
}
then you would use it like:
static content = {
form { $("#form").module(FormModule) }
}
form.button1().click()
I've created the code below to dynamically load 2 buttons into an element with an ID of masthead. Then a function called showMenus runs when each button is clicked, running some jQuery animations. Everything is wrapped inside of a RequireJS module.
The code works fine as is but I'm thinking it may be better to break it up into two separate RequireJS modules/files: one that loads the buttons on the page and another one that runs the showMenus function. I did refer to the RequireJS API docs but couldn't find an answer.
Any help is appreciated...thanks in advance!
require(['jquery'], function ($) {
var header = document.getElementById("masthead"),
$navMenu = $("#site-navigation-list"),
$searchBox = $("#searchform"),
menuButton = document.createElement("div"),
searchButton = document.createElement("div"),
showMenus;
$(menuButton).attr("id", "menu");
$(searchButton).attr("id", "search");
header.appendChild(searchButton);
header.appendChild(menuButton);
// break the code below into its on RequireJS module?
showMenus = function(btn,el) {
$(btn).click(function() {
if (el.is(":visible") ) {
el.slideUp({
complete:function(){
$(this).css("display","");
}
});
} else {
el.slideDown();
}
});
};
showMenus(menuButton, $navMenu);
showMenus(searchButton, $searchBox);
});
What follows is only my opinion, but you might find it useful.
It might help to think in terms of things that your app is made of, and then maybe they are candidates for modules. So in your example, a 'masthead' seems to be a thing that you are interested in.
So using RequireJS, we can create a new module representing a generic masthead:
// Masthead module
define(['jquery'], function ($) {
function showMenus (btn, el) {
function toggle (el) {
if (el.is(":visible")) {
el.slideUp({
complete:function(){
$(this).css("display","");
}
});
} else {
el.slideDown();
}
}
$(btn).click(function() {
toggle(el);
});
}
// A Masthead is an object that encapsulates a masthead DOM element.
// This is a constructor function.
function Masthead (mastheadElement) {
// 'this' is the masthead object that is created with the 'new'
// keyword in your application code.
// We save a reference to the jQuerified version of mastheadElement.
// So mastheadElement can be a DOM object or a CSS selector.
this.$mastheadElement = $(mastheadElement);
}
// Add a method to Masthead that creates a normal button
Masthead.prototype.addButton = function (id) {
var $btn = $("<div/>").attr("id", id);
this.$mastheadElement.append($btn);
return $btn;
};
// Add a method to Masthead that creates a 'toggling' button
Masthead.prototype.addTogglingButton = function (id, elementToToggle) {
// ensure we have a jQuerified version of element
elementToToggle = $(elementToToggle);
// Reuse the existing 'addButton' method of Masthead.
var $btn = this.addButton(id);
showMenus($btn, elementToToggle);
return $btn;
};
// return the Masthead constructor function as the module's return value.
return Masthead;
});
And then use this module in our actual application code:
// Application code using Masthead module
require(["Masthead"], function (Masthead) {
// We create a new Masthead around an existing DOM element
var masthead = new Masthead("#masthead");
// We add our buttons.
masthead.addTogglingButton("menu", "#site-navigation-list");
masthead.addTogglingButton("search", "#searchform");
});
The advantage of this approach is that no DOM ids are hard-coded into the module. So we can reuse the Masthead module in other applications that require this functionality, but which may be using different DOM ids.
It might be convenient to think of this as separating the what things are from the how we use them.
This is a simple example, but frameworks/libraries like Backbone and Dojo (and many, many more) take this further.
I have a requirement to extend the YUI Panel with some custom functionality that will be in a new file and shared across multiple views.
I am at a bit of a loss as to how best to go about this, can anyone give me any pointers please?
Let's say you want to extend a Panel to create one that has a list in its body. I usually use Y.Base.create for this. It's a more declarative way of extending YUI classes than using a constructor and Y.extend. But I'll stay closer to your example in the YUI forums.
There are a couple of tricks dealing with WidgetStdMod (one of the components of Y.Panel), but mostly it's just about using Y.extend and following the YUI inheritance patterns. I'll try to answer with an example:
function MyPanel() {
MyPanel.superclass.constructor.apply(this, arguments);
}
// hack: call it the same so you get the same css class names
// this is good for demos and tests. probably not for real life
MyPanel.NAME = 'panel';
MyPanel.ATTRS = {
listItems: {
// YUI now clones this array, so all's right with the world
value: []
},
bodyContent: {
// we want this so that WidgetStdMod creates the body node
// and we can insert our list inside it
value: ''
}
};
Y.extend(MyPanel, Y.Panel, {
// always a nice idea to keep templates in the prototype
LIST_TEMPLATE: '<ul class="yui3-panel-list"></ul>',
initializer: function (config) {
// you'll probably want to use progressive enhancement here
this._listContainer = Y.Node.create(this.LIST_TEMPLATE);
// initializer is also the place where you'll want to instantiate other
// objects that will live inside the panel
},
renderUI: function () {
// you're inheriting from Panel, so you'll want to keep its rendering logic
// renderUI/bindUI/syncUI don't call the superclass automatically like
// initializer and destructor
MyPanel.superclass.renderUI.call(this);
// Normally we would append stuff to the body in the renderUI method
// Unfortunately, as of 3.5.0 YUI still removes all content from the body
// during renderUI, so we either hack it or do everything in syncUI
// Hacking WidgetStdModNode is doable but I don't have the code around
// and I haven't memorized it
//var body = this.getStdModNode('body');
},
syncUI: function () {
// same here
MyPanel.superclass.syncUI.call(this);
// insert stuff in the body node
var listContainer = this._listContainer.appendTo(this.getStdModNode('body'));
Y.Array.each(this.get('listItems'), function (item) {
listContainer.append('<li>' + item + '</li>');
});
}
});
a little question about erazor https://github.com/ciscoheat/erazor
i know this framwork is based on Razor template engine. http://weblogs.asp.net/scottgu/archive/2010/07/02/introducing-razor.aspx
i noticed the api doesn't fit exactly with Razor (ex: #for(a in p) differs from RAZOR)
this template system for haxe is very handy...
i just don't know how to setup a variable like we do in templo ( :: set mock="tada!":: )
//#scope is mycontroller;
#{var mock = scope.getMock()}
#if(mock!=null){
//display some html
}
any tips ?
thx
The following snippet works:
import erazor.Template;
import neko.Lib;
class Main {
static function main() {
var template = new Template("#{var mock = scope.getMock();} #if (mock != null) { #mock }");
Lib.print(template.execute( { scope : { getMock : function() return "hi" } } ));
}
}
What you missed is that inside a code block all the statements must be correctly closed (missing ;). Also erazor is loosely based on Razor and uses the Haxe syntax for expressions.