I have a dropdown in SPFX webpart in sharepoint online. In that dropdown, onchange, I am constructing a url with # tag.
E.x. https://sharepointonine/default.aspx#2349-234234-23434
I need to navigate to this new url. I am not sure how to accomplish things.
I have tried:
window.location = url //Gives error that string is not assignable to Location
window.location.href= url//does not reload the page
window.open(url, "_self")//does not reload the page
window.location.assign(url);//does not reload the page
window.location.replace(url);//does not reload the page
Any help?
You can create element 'a' and call click to open url
let a = document.createElement('a');
a.href = 'your link to open';
a.click();
this works fine.
Also you can use react-router, as describe here
There are also redirect link:
import { Redirect } from 'react-router';
When you need to redirect to som url, you render redirect:
<Redirect to={'/to url'}></Redirect>
I test with no framework SPFX.
Test result:
Test code for your reference:
public render(): void {
this.domElement.innerHTML = `
<div class="${styles.noframeworkSpfx}">
<div class="${styles.container}">
<div class="${styles.row}">
<div class="${styles.column}">
<span class="${styles.title}">Welcome to SharePoint!</span>
<p class="${styles.subTitle}">Customize SharePoint experiences using Web Parts.</p>
<p class="${styles.description}">${escape(this.properties.description)}</p>
<a href="https://aka.ms/spfx" class="${styles.button}">
<span class="${styles.label}">Learn more</span>
</a>
<select id="option" >
<option value="test1">test1</option>
<option value="test2">test2</option>
<option value="test3">test3</option>
</select>
</div>
</div>
</div>
</div>`;
this.domElement.querySelector('#option').addEventListener('change', (e) => {
window.location.href="https://www.google.com/search?q="+e.target["value"]
})
}
Related
I'm trying to web scrape content from 2 different divs that are on the same level. I'm using NodeJS, Axios, Cheerio and Express.
Basically, I'm trying to collect an image and the info related to it, but they are placed of different divs that are on the same level. Using the "main" doesn't seem to work in my case.
<div class="main">
<div class="one">
// image
</div>
<div class="two">
// info
</div>
</div>
Below is my code to get the data from a website:
var leafletList = $('.store-flyer__info', html).each(function() {
let leaflet = {
title: $(this).find('h3').text(),
image: $(this).find('source').attr('srcset'),
link: $(this).find('a').attr('href'),
validDate: $(this).find('small').text().slice(3,-1)
}
leaflets.push(leaflet)
})
Below is the website's HTML:
The way my code is right now, it's obviously getting only the title, link and validDate. But anyone knows how can I get the the srcset from the other div? I've also tried the following method, but it doesn't work:
var leafletList = $('.store-flyers', html).each(function() {
let leaflet = {
title: $(this).find('.store-flyer__info h3').text(),
image: $(this).find('.store-flyer__front source').attr('srcset'),
link: $(this).find('.store-flyer__info a').attr('href'),
validDate: $(this).find('.store-flyer__info small').text().slice(3,-1)
}
leaflets.push(leaflet)
})
There are many ways to get the result based on the HTML snippet you show, with the caveat that the developer tools can be misleading. It shows elements created after page load with JS, which you won't have if you're only requesting the raw page HTML.
With that in mind, here are a few options:
const cheerio = require("cheerio"); // ^1.0.0-rc.12
const html = `
<div class="store-flyer">
<picture>
<source srcset="foo.jpeg" type="image/webp">
<source srcset="bar.jpeg" type="image/jpeg">
</picture>
</div>
<div class="store-flyer">
<picture>
<source srcset="quux.jpeg" type="image/webp">
<source srcset="garply.jpeg" type="image/jpeg">
</picture>
</div>
`;
const $ = cheerio.load(html);
const result = [...$(".store-flyer")].map(e => ({
// select using `.first()` and `.last()` Cheerio methods:
firstImage: $(e).find("source").first().attr("srcset"),
secondImage: $(e).find("source").last().attr("srcset"),
// select using CSS attribute selectors:
firstImageByType: $(e).find('source[type="image/webp"]').attr("srcset"),
secondImageByType: $(e).find('source[type="image/jpeg"]').attr("srcset"),
// select as an array of all <source> elements:
allImages: [...$(e).find("source")].map(e => $(e).attr("srcset")),
}));
console.log(result);
Output:
[
{
firstImage: 'foo.jpeg',
secondImage: 'bar.jpeg',
firstImageByType: 'foo.jpeg',
secondImageByType: 'bar.jpeg',
allImages: [ 'foo.jpeg', 'bar.jpeg' ]
},
{
firstImage: 'quux.jpeg',
secondImage: 'garply.jpeg',
firstImageByType: 'quux.jpeg',
secondImageByType: 'garply.jpeg',
allImages: [ 'quux.jpeg', 'garply.jpeg' ]
}
]
Prepending .store-flyer__front to your source selectors might be a good idea if you need to disambiguate.
With cheerio, you can access node properties such as:
parentNode
previousSibling
nextSibling
nodeValue
firstChild
childNodes
lastChild
<div class="main">
<div class="one">
// image
</div>
<div class="two">
// info
</div>
</div>
.main.firstChild is .one
.one.nextSibling is .two
.main.lastChild is .two
.two.previousSibling is .one
i'm trying to switch between home and profile page for same user with click on each button. also except change between home and profile,id should stay the same.
but after click on profile button only id will be changed and it uses profile as id
(i used ejs as format for my views/html pages)
any idea how can i fix it?is that even possible?
there is my nav code:
<nav>
<div class="nav-wrapper teal darken-4">
BAZAART
<ul class="right hide-on-med-and-down">
<li><a class="waves-effect waves-light btn teal lighten-1" href="home"> <i class="material-icons right">home</i> home</a></li>
<li><a class="waves-effect waves-light btn teal lighten-1" href="profile">profile <i class="material-icons right">account_box</i></a></li>
</ul>
</div>
</nav>
homeController:
exports.sendReqParam = (req, res) => {
let userHome = req.params.userHome;
res.render("home", { name: userHome });
// res.send(`This is the homepage for ${userHome}`);
};
exports.respondWithName = (req, res) => {
let paramsName = req.params.myName;
res.render("profile", { name: paramsName });
}
main.js
app.get("/profile/:myName", homeController.respondWithName);
app.get("/", homeController.respondInfo);
app.get("/home/:userHome", homeController.sendReqParam)
I was recently making a blog website, where I write a post and it displays it on the home page. But if we wanted to go to the specific post page, instead of making another separate page for each new post, we made a post.ejs page instead, and later to acces the specific post we simply used something called lodash. I'll show you an example of it, so it makes more sense, and I'll show you the code we used.
So the example is this, I go to the compose.ejs page and I write a random post: title=Post, content=A random lorem ipsum
and lets say we write another post: title=Another post, content=Another random lorem ipsum
Okay so now everytime we write a blog post it sends us to the home page (where we currently are) and it shows the two blogs posts. If we wanted to go to the specific url of the post, we simply write this link localhost:3000/posts/Another post hit enter and it takes us to the second post we wrote.
And this is the code we used inside the app.js:
app.get("/posts/:postName", function(req, res){
const requestedTitle = _.lowerCase(req.params.postName);
posts.forEach(function(post) {
const storedTitle = _.lowerCase(post.title);
if (storedTitle === requestedTitle) {
res.render("post", {title: post.title, content: post.content});
}
});
});
In the app.js code, we see in the app.get /posts/:postName and this is just the name that is going to show in the url, :postName is like a variable and it will store whatever the user writes.
In the second line, we use lodash to rewrite what the user wrote to what we want, for example if the user wrote AnoTheR POst it will automatically change it to another-post, and we store it in a constant called requestedTitle.
Next is a forEach loop on a posts array (where we store every post), and this is just to go throught every post and check the names.
In the 4th line, we are again using lodash for the same thing, but this time arround for the title of each individual post, and storing it in a constant called storedTitle.
And last, an if statement, where if both the names are the same then it will render the post.ejs page, and we just pass down the title and content from the selected post using this code , {title: post.title, content: post.content}.
And this is the code we used inside the post.ejs:
<%- include("partials/header") -%>
<br>
<div class="card">
<h2 class="card-header"> <%= title %> </h2>
<div class="card-body">
<p class="card-text"> <%= content %> </p>
</div>
</div>
<%- include("partials/footer") -%>
As you can see this post.ejs isn't hard to explain, the top and bottom lines where it says include("partials are just the header and footer templates I use, just to save time coding. Whats inside is what the post.ejs will render when it gets called.
I hope it wasn't that confusing, I'm still learning to code and I hope it helps you with what you are looking for. I think this isn't the exact answer for your question, but I think it will help you navigate your way throught.
If you need more explanation or help, this is my instagram: #cemruniversal, I'm always happy to help if I can.
Edit: 30 minutes after original post
I think I found a way it could work, I'll show you a piece of code from the same blog website.
Whenever I want to compose a new post I use this code:
app.get("/compose", function(req, res){
res.render("compose");
});
And obviously there is a form for you to write the post, and after you submit, it sends you to the home page, and saves the post. For that I used this piece of code:
app.post("/compose", function(req, res){
const post = {
title: req.body.postTitle,
content: req.body.postBody
};
posts.push(post);
res.redirect("/");
});
I had an idea for your website, what if when you pressed the Profile button, it renders a specific page on your site, and when you press another button it renders another page. It could work, wouldn't it?
Please try it out and tell me how it went.
I think something like this:
<nav>
<div class="nav-wrapper teal darken-4">
BAZAART
<ul class="right hide-on-med-and-down">
<li><a class="waves-effect waves-light btn teal lighten-1" href="/home"> <i class="material-icons right">home</i> home</a></li>
<li><a class="waves-effect waves-light btn teal lighten-1" href="/profile">profile <i class="material-icons right">account_box</i></a></li>
</ul>
</div>
</nav>
I am creating list of items looped through .map function. I want each of these items be rendered in a single page with some other details.
import React from 'react'
import {faArrowRight, faMusic, faPlay, faPlayCircle, faTachometerAlt} from "#fortawesome/free-solid-svg-icons";
import {FontAwesomeIcon} from "#fortawesome/react-fontawesome";
import music from '../mocks/music.json'
import { Link } from 'gatsby'
import Music from '../pages/music'
const newData = music.map( (data) => {
return (
<div className="row no-gutters justify-content-between align-items-center">
<div className="col-auto">
<button className="btn-gradient btn-circle">
<FontAwesomeIcon icon={faPlayCircle} />
</button>
</div>
<div className="col">
<div className="music-list-content">
<span className="artist">{data.author}</span>
<Link to={`/music/${data.id}`}>{data.title}</Link>
<span className="play">
<FontAwesomeIcon icon={faPlay} /> {data.duration}
</span>
</div>
</div>
<div className="col-auto">
<span className="badge-dark badge">{data.genre}</span>
</div>
</div>
)
})
const membersToRender = music.filter(member => member.id)
const numRows = membersToRender.length
const Musics = () => {
return (
<div>
<div className="title">
<h5>New Music</h5>
<span>{numRows} new songs</span>
</div>
<div>
<div className="music-list card-wrapper">
{newData}
</div>
</div>
<div className="footer-wrapper">
<div>
<FontAwesomeIcon icon={faMusic} />
<span>Song Library</span>
</div>
<FontAwesomeIcon icon={faArrowRight} />
</div>
</div>
)
}
export default Musics
I created a link which whenever I click, it takes me to another page (page not found) with id appended and .js extension.
Please, how do go about it? I want a click on the title and have it displayed on a full page.
Your logic seems good, however, you are missing the most important part, the page creation, since you are not creating the pages, all of your links are broken.
In Gatsby, you have two different ways of creating pages:
Using gatsby-node.js to create pages dynamically: when dealing with a huge amount of data, like your JSON, it's easier to let Gatsby deal with this responsibility of creating pages for Gatsby. Since you are sourcing from a JSON, you need everything set to create dynamic pages.
const path = require("path")
// Implement the Gatsby API “createPages”. This is called once the
// data layer is bootstrapped to let plugins create pages from data.
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const musics= require("./data/mocks/musics.json")
const musicTemplate = path.resolve(`src/templates/music-template.js`)
musics.forEach(music) => {
createPage({
path: `/music/${music.slug}`
component: musicTemplate,
context: {
title: music.title,
description: music.description,
// and so on for the rest of the fields
},
})
})
}
Note: I'm assuming that your JSON is properly defined and formatted, having all the fields I queried.
Your musicTemplate must be a template (inside /templates folder).
Notice that you are passing some fields through Gatsby's context, this means that those fields will be available through props.pageContext in your template. So, there, create a template like:
import React from "react"
import Layout from "../components/layout"
export default function MusicTemplate({pageContext}) {
return (
<Layout>
<div>Hello musician {pageContext.title}</div>
</Layout>
)
}
So, as I said, with this approach you are creating dynamic pages based on your JSON file, and they will be available inside localhost:8000/music/{music.slug}, and all your reference and links that point there, will be valid.
I would also recommend using static query/useStaticQuery to retrieve data from your JSON in that loop. If you create a static query from that data (in a separate component) you will be able to fetch it on-demand across your project, so you will be reusing an interesting part of logic. It's better to use it rather than requesting a JSON directly.
You can follow this guide from the great Jason Lengstorf which is mostly what you need.
Adding .js files in your /pages folder: Gatsby infers the internal structure of your /pages folder and will create pages accordingly to that structure. For instance, if you have a structure like: /pages/musicians/name1.js Gatsby will create a page like localhost:8000/musicians/name1.
As it has been said, the first approach fits your requirements and it's preferred for this use-cases, since the second one will be less scalable and maintainable.
You should do some routing with React-Router (https://reactrouter.com/web/example/basic).
So the link have to point to a Route in a Switch, as is in the example of the link.
I'm using Selenium and Python to try and click on each of the links shown. the same class name is used for the different websites.
`<div class="rp-table-col c7">
<span class="mobile-title">Action</span>
<a class="action" href="https://website.com" target="_blank">View</a>
</div>
<div class="rp-table-col c7">
<span class="mobile-title">Action</span>
<a class="action" href="https://website2.com" target="_blank">View</a>
</div>`
<div class="rp-table-col c7">
<span class="mobile-title">Action</span>
<a class="action" href="https://website3.com" target="_blank">View</a>
</div>`
<div class="rp-table-col c7">
<span class="mobile-title">Action</span>
<a class="action" href="https://website4.com" target="_blank">View</a>
</div>`
so far I'm able to get the 'View' text of all the classes but what I want to do is click on each of the links in the href (website, website2, website3, etc...)
y_list = driver.find_elements_by_css_selector("div[class*='rp-table-col c7']")
You can select all links and click them one by one using:
from selenium import webdriver
driver = webdriver.Chrome()
# Some code
links = [div.find_element_by_tag_name('a') for div in driver.find_elements_by_class_name('rp-table-col c7')]
for link in links:
link.click()
Warning! You must consider the possibility if clicking them switched the control to the new tab. So, I think you have the URLs saved in the links (list of <a> tags) variable, which you must use one by one by navigating to each URL. The links[0].get_attribute('href') will give you the first webdite URL.
I am very new to Orchard.
I have created a new theme, based on the Minty theme. The only real change is the layout, where I have adapted the html from an existing asp.net masterpage to match the orchard style razor layout.cshtml. I have experience with MVC and razor, so no problem on that side... unless I have missed something vital.
The problem is the login page. Clicking the sign in link takes me to the correct url without errors, but not login form gets rendered. I have checked that this is the case by Inspecting Element in google chrome.
I am aware that setting up widgets, etc, I can make content appear. However, I can't find how the login form gets inserted when the login url gets requested. I presume it uses the Orchard.Users module, but not sure how. Does it need a specific zone? I can't see why, but see how else.
As a result, I can't solve my problem...
Any pointers?
Any books or other learning media?
The code for my layout.cshtml is:
#functions {
// To support the layout classifaction below. Implementing as a razor function because we can, could otherwise be a Func<string[], string, string> in the code block following.
string CalcuClassify(string[] zoneNames, string classNamePrefix) {
var zoneCounter = 0;
var zoneNumsFilled = string.Join("", zoneNames.Select(zoneName => { ++zoneCounter; return Model[zoneName] != null ? zoneCounter.ToString() : "";}).ToArray());
return HasText(zoneNumsFilled) ? classNamePrefix + zoneNumsFilled : "";
}
}
#{
/* Global includes for the theme
***************************************************************/
SetMeta("X-UA-Compatible", "IE=edge,chrome=1");
Style.Include("http://fonts.googleapis.com/css?family=Handlee");
Style.Include("http://html5shiv.googlecode.com/svn/trunk/html5.js");
Style.Include("site.css");
Script.Require("jQuery").AtHead();
Script.Require("jQueryUI_Core").AtHead();
Script.Require("jQueryUI_Tabs").AtHead();
Script.Include("http://cdnjs.cloudflare.com/ajax/libs/modernizr/2.0.4/modernizr.min.js").AtHead();
Style.Include("TagDefaults.css");
Style.Include("LayoutStructure.css");
Style.Include("LayoutStyling.css");
Style.Include("TopMenu.css");
Style.Include("LeftBlock.css");
Style.Include("RightBlock.css");
Style.Include("MenuAdapter.css");
Style.Include("Content.css");
Style.Include("FloatedBoxes.css");
Style.Include("Helen.css");
/* Some useful shortcuts or settings
***************************************************************/
Func<dynamic, dynamic> Zone = x => Display(x); // Zone as an alias for Display to help make it obvious when we're displaying zones
/* Layout classification based on filled zones
***************************************************************/
//Add classes to the wrapper div to toggle aside widget zones on and off
var asideClass = CalcuClassify(new [] {"Sidebar"}, "aside-"); // for aside-1, aside-2 or aside-12 if any of the aside zones are filled
if (HasText(asideClass)) {
Model.Classes.Add(asideClass);
}
//Add classes to the wrapper div to toggle tripel widget zones on and off
var tripelClass = CalcuClassify(new [] {"TripelFirst", "TripelSecond", "TripelThird"}, "tripel-"); // for tripel-1, triple-2, etc. if any of the tripel zones are filled
if (HasText(tripelClass)) {
Model.Classes.Add(tripelClass);
}
//Add classes to the wrapper div to toggle quad widget zones on and off
var footerQuadClass = CalcuClassify(new [] {"FooterQuadFirst", "FooterQuadSecond", "FooterQuadThird", "FooterQuadFourth"}, "split-"); // for quad-1, quad-2, etc. if any of the quad zones are filled
if (HasText(footerQuadClass)) {
Model.Classes.Add(footerQuadClass);
}
var slideshowClass = CalcuClassify(new[] {"HomeSlideshow"}, "slideshow-");
if (HasText(slideshowClass)) {
Model.Classes.Add(slideshowClass);
}
/* Inserting some ad hoc shapes
***************************************************************/
//WorkContext.Layout.Header.Add(New.Branding(), "5"); // Site name and link to the home page
//WorkContext.Layout.Footer.Add(New.BadgeOfHonor(), "5"); // Powered by Orchard
WorkContext.Layout.Footer.Add(New.User(), "10"); // Login and dashboard links
/* Last bit of code to prep the layout wrapper
***************************************************************/
Model.Id = "layout-wrapper";
var tag = Tag(Model, "div"); // using Tag so the layout div gets the classes, id and other attributes added to the Model
}
#tag.StartElement
<a name="top"></a>
<div id="SiteHeader">
</div>
<div id="PageContainer">
<div style="position: absolute; Left:-80px; top:-88px;z-index:1000;">
<img id="bird" title="Pheasant" src="/Themes/TheFarmsBlogs/Styles/Images/PositionedImages/pheasant.gif" />
</div>
<div class="SiteMenu"><p>Hello Menu</p></div>
<div id="Specialized">
<div id="PageName">
<!--
PageName NOT in use!
-->
</div>
#if (Model.RightColumn != null) {
<div id="RightCol">
#Zone(Model.RightColumn)
</div>
}
<!-- Page divided into two main columns, of which the left column is subdivided as necessary -->
<div id="LeftCol">
<div id="PageBanner">
<div id="PageBannerLeft">
#if (Model.MainImage != null) {
<div id="PageBannerImage">
#Zone(Model.MainImage)
</div>
}
#if(Model.TheStrip != null) {
<div id="TheStrip">
#Zone(Model.TheStrip)
</div>
}
</div>
</div>
<div id="SpecializedContent">
#if(#Model.content != null)
{
#Zone(Model.content)
}
</div>
</div>
<div id="SpecializedFooter">
</div>
</div>
<div id="PageFooter">
#if (Model.FooterPage != null){
#Zone(Model.FooterPage)
}
</div>
</div>
<div id="SiteFooter">
#Display(Model.Footer)
The Farms Ltd - © 2007
</div>
#tag.EndElement
PS: the branding and badge of honour are commented out as I am only enabling bit by bit to eliminate the source of errors. It will be in the live site.
ADDENDUM:
See Bertrand Le Roy's answer below. The Orchard.Users module requires a Content zone with a Capital C. That instantly cured the problem.
I added this as Bertrand's response was tentative, and I wanted to reinforce that the problem was the name of the zone.
In Orchard.Users, look for Controllers/AccountController.cs. In there, there is a LogOn action. It creates a LogOn shape that it then puts in a shape result. This then gets resolved as the Views/LogOn.cshtml template (which you can override in your theme by just dropping a file with the same name in there, for example a copy of the original that you can tweak). The LogOn template will be rendered within the theme's layout, in the Content zone. Does this answer your question?
I think the mistake you made was to name your Content zone content (notice the casing).