For building a vue menu in October, I have a plugin that I want to extract the October pages structure in a JSON data keeping the pages and subpages indentation.
Based on this post : How to get static page dropdown in OctoberCMS with get page tree?
I used the following code :
public function boot() {
\RainLab\Pages\Classes\Page::extend(function($model) {
$model->addDynamicMethod('getPageOptions', function() {
$theme = \Cms\Classes\Theme::getEditTheme();
$pageList = new \RainLab\Pages\Classes\PageList($theme);
$treePageList = $pageList->getPageTree(true);
$pages = [];
$this->getRecursivePage($pages, $treePageList);
return $pages;
});
});
}
public function getRecursivePage(&$pages, $subpages, $level = 0) {
$level++;
foreach($subpages as $pageArr) {
$pages[$pageArr->page->url] =
str_repeat('-',$level) . ' ' . $pageArr->page->title;
if(count($pageArr->subpages) > 0) {
$this->getRecursivePage($pages, $pageArr->subpages, $level);
}
}
}
but the returned $treePageList is too rich for that purpose and the $pages flattens the indentation.
How could I manipulate the returned JSON structure to simplify it, with only page->url and page->title and keeping the pages and subpages indentation ?
Thanks for help
EDIT :
This code with the $level produces :
array:9 [▼
"/content" => "- Content"
"/content/pages" => "-- Static Pages"
"/content/content" => "-- Content"
"/content/models" => "-- Models"
"/content/urls" => "-- URLs"
"/content/urls/tesets" => "--- tesets"
"/test-sp" => "- test-sp"
"/test-sp/oks" => "-- oks"
"/test" => "- test"
]
but I'd like to have a JSON data with levels like (not raw data visualization) :
▼ 0
page {title: , url:}
subpages []
▼ 1
page {title: , url:}
subpages
▼ 0 {title: , url:}
▼ 1 {title: , url:}
▼ 2 {title: , url:}
▼ 3 {title: , url:}
▼ 4 {title: , url:}
▼ 5 {title: , url:}
▼ 6 {title: , url:}
▼ 7 {title: , url:}
▼ 8 {title: , url:}
▼ 2
page {title: , url:}
subpages
▼ 0 {title: , url:}
▼ 1 {title: , url:}
▼ 2 {title: , url:}
Use this code
public function boot() {
\RainLab\Pages\Classes\Page::extend(function($model) {
$model->addDynamicMethod('getPageOptions', function() {
$theme = \Cms\Classes\Theme::getEditTheme();
$pageList = new \RainLab\Pages\Classes\PageList($theme);
$treePageList = $pageList->getPageTree(true);
return $this->getRecursivePage($treePageList);
});
});
$pages = \RainLab\Pages\Classes\Page::getPageOptions();
header('Content-Type: application/json');
echo json_encode($pages);
exit();
}
public function getRecursivePage($pages) {
$pageDetails = [];
foreach($pages as $iPage) {
$detail = [];
$detail['page'] = ['title' => $iPage->page->title, 'url' => $iPage->page->url];
$subpages = $this->getRecursivePage($iPage->subpages);
if(count($subpages) > 0 ) {
$detail['subpages'] = $subpages;
}
$pageDetails[] = $detail;
}
return $pageDetails;
}
Output
[
{
"page": {
"title": "static-page",
"url": "/static-page"
}
},
{
"page": {
"title": "/parent",
"url": "/parent"
},
"subpages": [
{
"page": {
"title": "child",
"url": "/parent/child"
},
"subpages": [
{
"page": {
"title": "another child",
"url": "/parent/child/another-child"
}
},
{
"page": {
"title": "another next",
"url": "/parent/child/another-next"
}
}
]
}
]
}
]
If any doubt please comment.
I solved this by adding 2 functions and calling them sequentially. One for Pages an one for Static Pages.
The functions are building a complex object with the desired structure which will be use for building the menu.
Thanks
Related
I'm new to MongoDB,Can someone provide MongoDB schema for the below data :
{ listData :
[
[ { title: 'test' } ],
[
{ listmessage: 'message 1' },
{ listmessage: 'message 2' },
{ listmessage: 'message 3' }
]
]
}
I've tried this below schema, but it's not working :
const listSchema = mongoose.Schema(
{ listData: [
[{title:{type:String}}],
[{listmessage :{type:String}}]
]
})
Am i doing something wrong ?
First I create a website by vue and I pass parameter to my google appscript
vue function
var appurl = "my google appscript url"
this.$http.get(appurl, {
params: {
settingLink: this.settingLink,
albumLink: this.albumLink,
stepControl: this.stepControl,
nav: this.navSetting,
}
}).then(response => {
console.log(response);
})
and I send parameter to google appscript
my google appscript
function doGet(e) {
var params = e.parameter;
var settingLink = params.settingLink;
var albumLink = params.albumLink;
var stepControl = params.stepControl;
var nav = params.nav;
var SpreadSheet = SpreadsheetApp.openByUrl(settingLink);
var SheetName = SpreadSheet.getSheetByName("gameSetting");
SheetName.getRange("D2").setValue(stepControl);
SheetName.getRange("E2").setValue(albumLink);
SheetName.getRange("F2").setValue(nav[0].style);
SheetName.getRange("G2").setValue(nav[1].style);
SheetName.getRange("H2").setValue(nav[2].style);
}
The parameter albumLink and stepControl are string so I success push it to my spreadsheet (figure 1)
but nav is an array object like (in vue data)
nav=[
{style:"red",content:"test"},
{style:"blue",content:"test"},
{style:"green",content:"test"},
]
How could I let spreadsheet F2 as "red", G2 as "blue", and H2 as "green"
figure 1
enter image description here
By checking the output of JSON.stringify(e), it seems that each property of objects become different parameters and are received as something like the following:
{
"queryString": "settingLink=settingLinkTest&albumLink=albumLinkTest&stepControl=stepControlTest&nav%5B0%5D%5Bstyle%5D=red&nav%5B0%5D%5Bcontent%5D=test&nav%5B1%5D%5Bstyle%5D=blue&nav%5B1%5D%5Bcontent%5D=test&nav%5B2%5D%5Bstyle%5D=green&nav%5B2%5D%5Bcontent%5D=test",
"contextPath": "",
"parameters": {
"nav[1][style]": [
"blue"
],
"nav[1][content]": [
"test"
],
"albumLink": [
"albumLinkTest"
],
"nav[2][style]": [
"green"
],
"settingLink": [
"settingLinkTest"
],
"nav[2][content]": [
"test"
],
"nav[0][style]": [
"red"
],
"stepControl": [
"stepControlTest"
],
"nav[0][content]": [
"test"
]
},
"contentLength": -1,
"parameter": {
"nav[2][content]": "test",
"nav[1][content]": "test",
"albumLink": "albumLinkTest",
"settingLink": "settingLinkTest",
"nav[0][style]": "red",
"stepControl": "stepControlTest",
"nav[0][content]": "test",
"nav[1][style]": "blue",
"nav[2][style]": "green"
}
}
Since the index of a particular nav and its properties become names of parameters, you can get their values by changing your doGet() to:
SheetName.getRange("F2").setValue(params["nav[0][style]"]);
SheetName.getRange("G2").setValue(params["nav[1][style]"]);
SheetName.getRange("H2").setValue(params["nav[2][style]"]);
Alternatively, as suggested in the comments of the question, you can stringify the nav object when sending it:
this.$http.get(appurl, {
params: {
settingLink: settingLink,
albumLink: albumLink,
stepControl: stepControl,
nav: JSON.stringify(nav)
}
}).then(response => {
console.log(response);
})
And then parsing when you receive it to use as you intended:
var nav = JSON.parse(params.nav);
SheetName.getRange("F2").setValue(nav[0].style);
SheetName.getRange("G2").setValue(nav[1].style);
SheetName.getRange("H2").setValue(nav[2].style);
I'm using vue.js in my project and I need to filter this json in my html and display only what is with areas = area_one, I am currently using one filter, but it displays all the elements of my json
JSON
{
"ID":789,
"title":"Title Page",
"image_desktop":"image21.jpg",
"image_mobile":"image234.jpg",
"link":"#",
"areas":[
"area_one"
]
},
{
"ID":765,
"title":"Title Page 2",
"image_desktop":"image231.jpg",
"image_mobile":"image421.jpg",
"link":"#\/link",
"areas":[
"area_two"
]
}
]
and vue HTML
<div v-for="example in filtered_examples" :key="example.ID">
<img :src="example.image_mobile">
<img :src="example.image_desktop">
</div>
I need to filter only one of the areas in my html
The filtered_example:
this.filtered_examples = !this.area ? this.examples : this.examples.filter(b => b.areas.indexOf(this.area) > -1 )
This works fine.
var examples = [{
"ID": 789,
"title": "Title Page",
"image_desktop": "image21.jpg",
"image_mobile": "image234.jpg",
"link": "#",
"areas": [
"area_one"
]
},
{
"ID": 765,
"title": "Title Page 2",
"image_desktop": "image231.jpg",
"image_mobile": "image421.jpg",
"link": "#\/link",
"areas": [
"area_two"
]
}
];
var area = 'area_one';
var filtered_examples = examples.filter(b => b.areas.indexOf(area) > -1);
console.log(filtered_examples);
I would like to use Alpha Vanatage stock data in my ExtWebComponent chart. How could I fetch the data and render it in a Cartesian Area chart?
If you've generated an ExtWebComponents project, you could add these 2 files and declare the web component html element tag.
Usage
In the html file like index.html declare my-chart-area which is defined in the web component below.
AreaChartComponent.html - HTML Template
<ext-cartesian
width="1000px"
height="600px"
downloadServerUrl="http://svg.sencha.io"
shadow="true"
insetPadding="25 35 0 10"
axes='[{
"type": "numeric" ,
"position": "left" ,
"fields": [ "1. open" ],
"label": { "rotate": { "degrees": "-30" } },
"grid": { "odd": { "fill": "#e8e8e8" } },
"title": { "text": "Alphabet Inc Stock Data" , "fontSize": "20" }
},
{
"type": "category",
"position": "bottom",
"fields": "time",
"grid": "true",
"title": { "text": "Monthly", "fontSize": "20" }
}]'
legend='{
"type": "sprite",
"position": "bottom"
}'
series='[{
"type": "area" ,
"xField": "time",
"yField": [ "1. open", "2. high", "3. low", "4. close" ],
"title": [ "open", "high", "low", "close" ],
"style": { "stroke": "black" , "lineWidth": "2", "fillOpacity": "0.8" },
"colors": ["#003f5c", "#58508d", "#bc5090", "#ff6361", "#ffa600"]
}]'
platformConfig='{
"phone": { "insetPadding": "15 5 0 0" }
}'>
</ext-cartesian>
AreaChartComponent.js - Web Component
import template from './AreaChartComponent.html'
Ext.require([
'Ext.chart.theme.Midnight',
'Ext.chart.theme.Green',
'Ext.chart.theme.Muted',
'Ext.chart.theme.Purple',
'Ext.chart.theme.Sky',
'Ext.chart.series.Area',
'Ext.chart.axis.Numeric',
'Ext.chart.axis.Category'
]);
class AreaChartComponent extends HTMLElement {
constructor() {
super()
}
connectedCallback() {
this.innerHTML = template;
this._fetchChartData();
}
disconnectedCallback() {
}
attributeChangedCallback(attrName, oldVal, newVal) {
}
/**
* Fetch the chart data from https://www.alphavantage.co/ using an API Key.
*
* TODO Fetch your api key here: https://www.alphavantage.co/support/#api-key
*/
_fetchChartData() {
let me = this;
let apiKey = 'demo';
let stockSymbol = 'GOOGL';
let url = `https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=${stockSymbol}&apikey=${apiKey}`;
fetch(url)
.then(response => {
return response.json();
})
.then(json => {
return me._flattenData(json);
})
.then(jsonflatRows => {
me._renderChart(jsonflatRows);
})
.catch(err => {
console.log("error", err);
})
}
/**
* The goal is to flatten the nested json data, so it's easy to consume in the charts.
* #param json data
* #returns {*[]} array of json data
* #private
*/
_flattenData(json) {
console.log("json=", json);
let jsonTimes = json['Monthly Time Series']
let flatRows = [];
for (let jsonTime in jsonTimes) {
let row = {
"time": jsonTime
};
let jsonNestedTime = jsonTimes[jsonTime];
for (let nestedKey in jsonNestedTime) {
row[nestedKey] = jsonNestedTime[nestedKey];
}
flatRows.push(row);
}
return flatRows.reverse();
}
_renderChart(jsonflatRows) {
console.log('_renderChart jsonflatRows=', jsonflatRows);
let store = Ext.create('Ext.data.Store', {
fields: ["time", "1. open", "2. high", "3. low", "4. close", "5. volume"]
});
store.loadData(jsonflatRows);
let areaChartEl = this.querySelector('ext-cartesian');
areaChartEl.ext.bindStore(store);
}
}
window.customElements.define('my-chart-area', AreaChartComponent);
Source
https://gist.github.com/branflake2267/4652a5d7188dfe0b33d3d02a808d8d74
Data:
{
"contextTag": {
"value": "Bittersweet",
"valueLabel": "Bittersweet"
},
"tags": [
{
"name": "tag",
"value": "Creamy"
},
{
"name": "tag",
"value": "Colorful"
},
{
"name": "tag",
"value": "Bright"
}
],
"rating": 5,
"userNickName": "HelloGames",
"userLocation": "UK",
"title": "Great!",
"reviewText": "Yada yada yada yada",
"submissionTime": "30 Nov 16"
},
I currently have this working for getting contextTag valueLabels:
this.props.reviewData.reviews.map(
(o) => {
return o.contextTag && o.contextTag.valueLabel ? o.contextTag.valueLabel.trim() : '';
}
)
And this for tags:
this.props.reviewData.reviews.map(
(o) => {
return o.tags && o.tags.value ? o.tags.value.trim() : '';
}
)
But it's coming back empty. How do I loop through tags to grab each of the values?
You can cache the tags, then map over it to get the values. Like below:
const tags = this.props.reviewData.reviews.tags;
const tags_values = ( tags ? tags.map((tag) => (tag.value ? tag.value : '' ) : []); // this an array of the tags values.
Your code does not return what you want because the tags attributes is an array of objects, so to get the tag values you have to map over it as I did above.
Hope this helped.