I am creating a breadcrumb nav similar to app.box.com with mongo, express and react. The bread crumb on that website doesn't use URI to create the breadcrumb. For example if /home/level1/level2/level3 is the breadcrumb and I click on level2 the request made to server is /folder/{folder_id} instead of /home/level1/level2 and the breadcrumb truncates to /home/level1/level2. To achieve this, how to structure the data in mongodb? And how to handle it in react to create the breadcrumb while making sure that it will work between refresh?
If I create a schema like
{ _id: "asdfasdf", name: "level2", type: "folder", path: "/home/level1/level2" }
and then in react split the path into an array to create a breadcrumb then I won't be able to make /folder/{folder_id} GET requests.
I can ignore the path, create an empty array state and then add path and URL to array. For example after initializing an empty array state, if I click on level1 button then { name: "level1", URL: "/folder/level1-id" } will be added to the array. Then I can use that array to construct breadcrumb. But this won't survive page refresh. Psuedo-code is as follows,
const [pathArr,setPathArr] = useState([]);
// Make initial request to /home
// add { name: "Home", URL: "/folder/0" } to pathArr
// parsed breadcrumb will /home
// display files and folders received
// click on level1 folder button
// Push { name: "level1", URL: "/folder/level1_id" } to pathArr
// parsed breadcrumb will /home/level1
// Do same for level2, pathArr = [ {home obj},{level1 obj}, {level2 obj} ]
// Click on level1 breadcrumb
// pathArr = [ { home object }, { level1 object } ]
}
Any other suggestion on mongodb schema or react nav are appreciated.
Related
I have two TaskList components that use the same query GET_TASKS.
Both use a different filter query variable which is passed down to them in props as queryVars.
I defined a standard merge function in type policies to merge the incoming and existing data together.
The TaskList component uses
const { data, fetchMore } = useQuery<Response, Variables>(GET_TASKS, { variables: queryVars })
to retrieve the data.
A Fetch more button has () => fetchMore({ variables: queryVars }) in the onClick attribute.
When I click on the Fetch more button on the left, the tasks on the right get updated as well, however, without its filter applied, so the data that come with Assigned to me filter are also put to the Assigned by me task list and vice versa.
The merge function basically rewrites every data object that uses the given query.
How do I tell Apollo to only update the data that is bound to the component where fetchMore is defined?
You should be able to add filter to the keyArgs property. This should create different cache results based on the filter.
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
tasks: {
keyArgs: ["filter"],
merge(existing, incoming, { args: { offset = 0 }}) {
//Custom merge
},
},
},
},
},
});
The title may be miss leading but I'm not really sure how do I ask this question correctly. Here is the problem: I'd like to query my own API(not created yet so I made placeholder data) for global settings which might change in the future and I will only need to rebuild the website instead of editing it manually, I want to create source node called CmsSettings and pass it to GraphQL (structure similar to site.siteMetadata) but I don't know how can I achieve that. What I achieved so far is to create a source node called allCmsSettings which has my data as an object in nodes array.
exports.sourceNodes = ({ actions, createNodeId, createContentDigest }) => {
const { createNode } = actions;
const myData = {
key: 123,
app_title: `The foo field of my node`,
...
}
const nodeContent = JSON.stringify(myData);
const nodeMeta = {
id: createNodeId(`my-data${ myData.key }`),
parent: null,
children: [],
internal: {
type: `CmsSettings`,
mediaType: `text/html`,
content: nodeContent,
contentDigest: createContentDigest(myData)
}
}
const node = Object.assign({}, myData, nodeMeta);
createNode(node);
}
Here is the query used to get the data of the source node
allCmsSettings {
edges {
node {
id
app_title
...
}
}
}
Creating a query results in an array of results(which I know is the result of creating source nodes) but I'd like to create that source so that I could query it like this and:
CmsSettings {
app_title
app_keywords
app_descriptions
app_logo_path
brand_name
...
}
You get the point. I was browsing the gatsby node API but I can't find how to achieve this.
Thank you for your help
Nevermind, the answer is pretty simple, if you are new to gatsby just like me the sourceNodes export creates 2 graphql fields for you with all prefix and camel case source node. The thing that I wanted to make is already there and is queryable with
cmsSettings {
app_title
app_keywords
app_descriptions
app_logo_path
brand_name
...
}
Notice the lowercase letter even though it was declared as CmsSettings. It seems that gatsby really does some magic under the hood.
I have one template, let's call it Template A that prints JSON data into a table, one column includes a button which is conditionally rendered when has_violations equals true.
An example of the table:
Table
What I want to accomplish is to take the driver_id that is associated with that particular row into the router link and have it passed onto a different template file let's call it Template B.
But how can I accomplish this using Vuex Store?
Sample JSON data:
{"driver_id":1,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"13","driver_trailer":"83","driver_status":"driving","has_violations":false},
{"driver_id":2,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"58","driver_trailer":"37","driver_status":"sleeping","has_violations":true},
{"driver_id":3,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"80","driver_trailer":"27","driver_status":"driving","has_violations":true},
Basic steps:
Get index of row on button click.
Get index of JSON data using value from Step 1.
Store the JSON data from Step 2 into Vuex.
Send user to Template B using router.
Retrieve data from Store when in Template B
Because you did not show your exact structure, the code below is just a basic structure.
Here's the code:
/* VUEX demo */
new Vuex.Store({
state: {
driver_data: undefined
},
mutations: {
recordDriver({ state }, payload){
state.driver_data = payload;
}
}
});
/* TEMPLATE A demo */
new Vue.component('template-a', {
data: function(){
return {
// Assume this is the JSON
driverJSON: [
{ driver_id: 1, driver_name: 'John Smith' },
{ driver_id: 2, driver_name: 'Bob John' }
]
};
},
methods: {
onButtonClicked: function(e){
const button = e.target;
const td = button.parentElement;
const tr = td.parentElement;
const indexOfTr = [...tr.parentElement.children].findIndex(row => row === tr);
const dataToStore = this.driverJSON[indexOfTr];
// Store data into $store
this.$store.commit('recordDriver', dataToStore);
// After storing, direct page using $router
this.$router.go({ ... });
}
}
});
/* TEMPLATE B demo */
new Vue.component('template-b', {
data: function(){
return {
// Get driver data using $store
driver: this.$store.state.driver_data
}
}
});
I like Yong's answer, but I would rather suggest you to pass the driverID as a prop to your route and then use a VueX getter to get the violations for the particular ID.
so I currently have my router paths set up as such:
{
component: List,
name: "account-list",
path: "list/:id",
// alias: "list/:id/edit_item/:item_id",
children: [
{
path: "edit_item/:item_id",
},
{
path: "*",
redirect: "/account/list/:id"
}
]
}
And let's say we have a url like this: http://localhost:8080/#/account/list/5bb17bdec7fb946609ce8bd4/edit_item/5bbc2d12aded99b39c9eadb9
How would I turn this into:
http://localhost:8080/#/account/list/5bb17bdec7fb946609ce8bd4
I am currently using
this.$router.back()
Which generally works well, but if I were to have accessed another item by directly pasting in its url, this.$router.back() would just revisit the previous url.
So is there a surefire way to guarantee that I would remove the child path?
Vue router will maintain current params when navigating so you should be able to navigate to the named parent route.
For example
this.$router.push({ name: 'account-list' })
The current :id param will be re-used.
If you wanted to make it dynamic, you could work your way up the current route's matched array. For example, to go to the current route's named parent
this.$router.push({
name: this.$route.matched[this.$route.matched.length - 2].name
})
This will only work on nested routes of course as the matched length for top-level routes is only 1.
For non-named routes you must construct the full path with all params interpolated into the string. You could do it by getting the destination routes path property and substituting param tokens with those in this.$route.params but that's going to get messy. Eg
// get parent path property
let path = this.$route.matched[this.$route.matched.length - 2].path
let realPath = path.replace(/:\w+/g, param =>
this.$route.params[param.substr(1)])
this.$router.push(realPath)
This won't work if your paths use advanced pattern matching.
You can move your child route up one level, so that it is not a child anymore. That way the id would show up in the this.$router.params object.
[
{
path: 'list/:id',
component: List,
name: "account-list"
},
{
path: 'list/:id/edit_item/:itemid',
component: Form,
name: "account-form"
}
]
You can do a do a $router.replace to replace the current path.
const id = this.$router.params['id'];
this.$router.replace({
name: 'account-form',
params: { id }
})
Store your list id 5bb17bdec7fb946609ce8bd4 in localStorage and get that localStorage in route script file.And add it your redirect path. For ex:
var listId = localStorage.getItem('listId');
// routh obj
{
component: List,
name: "account-list",
path: "list/:id",
// alias: "list/:id/edit_item/:item_id",
children: [
{
path: "edit_item/:item_id",
},
{
path: "*",
redirect: "/account/list/" + listId
}
]
}
get the list's id first before replacing the route.
let currentId = this.$route.params.id
this.$router.replace({
name: 'list',
params: { id: currentId }
})
also, it would be better if you name your sub routes.
from example: enter link description here
// Create an array object for the tree root and child nodes
var children = [
{
children: [
{
label: 'File X'
},
{
label: 'File Y'
}
],
expanded: true,
label: 'Root'
}
];
// Create a TreeView Component
tree = new Y.TreeView(
{
boundingBox: '#myTreeView',
children: children
}
).render();
if I add some attrs to children array objects like:
children: [
{
label: 'File X',
internalId: 24342234,
customAttr: 'unicid',
}
]
var tree.on('click',function(e){
tree.get('lastSelected');
});
I can't get them after tree rendering and clicking on this tree node.
All nodes have the following built-in properties:
data Object Arbitrary serializable data related to the node. Use this property to store any data that should accompany a node when that node is serialized to JSON.
id String Unique id for the node. If you don't specify a custom id when creating a node, one will be generated automatically.
but it does not work for me..
console.log(tree.get('lastSelected').get('label'));
[gives "File X"]
console.log(tree.get('lastSelected').get('customAttr'));
[gives "undefined"]
I think you may be confusing the Y.Tree class from YUI with the Y.TreeView class from AlloyUI. The descriptions you gave for id and data come from the documentation for Y.Tree.Node from YUI, which is used by Y.Tree.
AlloyUI's Y.TreeView class uses Y.TreeNode objects instead, which don't have the data attribute you're trying to use to store custom data. Take a look at its documentation: http://alloyui.com/api/classes/A.TreeNode.html. To do this with AlloyUI you'll need to add some code of your own. You could extend Y.TreeNode for example, adding the data attribute to the subclass, and use it instead. Or you can store the data you need in the main node of each Y.TreeNode instance, which you can get via get('boundingBox'), and then use setData and getData on it.
Even if Y.TreeNode had this data attribute though, you'd need to change your code to add the custom data you need inside the data key instead of adding them together with the other attributes. For example:
children: [
{
label: 'File X',
data: {
internalId: 24342234,
customAttr: 'unicid'
}
}
]
Then you'd be able to get it through:
console.log(tree.get('lastSelected').get('data').customAttr);