UI router nested views issue - angular-ui-router

I'm struggling once again with ui router.
I'm trying some nested views.
The main layout home.html is called when navigating to url/account/management/Company (route managed by Symfony). Then I want ui router to kick in.
looks like this:
<div ui-view="discrimSelector"></div>
<div ui-view="main"></div>
Then this is my js routing definition:
FlatView.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("");
$stateProvider
.state('flatView', {
url:"",
views: {
'main': {
controller:"flatViewHome",
templateUrl:Routing.generate('_NRtworks_FlatView_elementList')
},
'discrimSelector#flatView': {
templateUrl:Routing.generate('_NRtworks_FlatView_DiscrimSelector')
}
}
});
});
I've followed advices to use full names for the second view but it doesn't get loaded (the "main" template gets loaded great and I can see in the net tab that the file of the other template is actually fetched but not plugged into the ui-view).
what am I doing wrong ?

Remove the #flatView from discrimSelector#flatView.
The notation is uiview#state. You should use it only when you want to load another state's ui-view.
E.g. Imagine you had two nested states. When you are in the child state you want to load the parent's (any ancestor) ui-view then you would use this notation.
$stateProvider
.state('parent', {
views: {
'A': {
...
},
'B': {
...
}
}
})
.state('child', {
views: {
'C': {
...
},
'A#parent': {
...
}
}
});

Related

Contentful graphql one too many relationship

I am trying to achieve this: getArticleBySlugWithFilteredTags('tag1', 'tag2', 'tag3') using 1 query ( 1 request ) and avoid clientside filtering ( grab many and filter out with javascript ).
I have content type Article that has an entry type as list: Tag ( another custom content type ).
So there is a one too many relationship: an Article can have multiple Tags.
Now getting back to this: getArticleBySlugWithFilteredTags('tag1', 'tag2', 'tag3').
Attempt using custom content type: Tag
Query:
data: articleCollection(limit: 1, where: {
slug: "article-unique-1",
}) {
items {
title
tagsCollection(limit: 5) { // here it would be nice if I can use "where": {name: "tag1"}
items {
name
value
linkedFrom {
relatedArticles: articleCollection(limit: 7) { // other related articles that has the same tag as parent Article
items {
slug
title
category
}
}
}
}
}
}
}
}
The only thing that is missing here is the that I need to filter out the tagsCollection ( based on some property: name or value ).
I see that I am limited to use "where" on tagsCollection.
Attempt using contentfulMetadata tags
Query
{
data: articleCollection(where:
{
slug: "article-unique-1",
contentfulMetadata: {
tags_exists: true,
tags: {
id_contains_some: ["tag1", "tag2"]
}
}
}) {
items {
contentfulMetadata {
tags {
id
name
linkedFrom { // I can't use this here
relatedArticles: articleCollection(limit: 7) {
items {
slug
title
category
}
}
}
}
}
slug
title
publicationDate
}
}
}
With this approach I am not able to use the linkedFrom in order to get also other related articles that have the same contentfulMetadata tags. What should I do in other to achieve this making 1 query and no clientside filtering with javascript ?

How to modify just a property from a dexie store without deleting the rest?

I'm having the dexie stores showed in the print screen below:
Dexie stores print screen
My goal is to update a dexie field row from a store without losing the rest of the data.
For example: when I edit and save the field "com_name" from the second row (key={2}) I want to update "com_name" only and not lose the rest of the properties, see first and the third row.
I already tried with collection.modify and table.update but both deleted the rest of the properties when used the code below:
dexieDB.table('company').where('dexieKey').equals('{1}')
//USING table.update
//.update(dexieRecord.dexiekey, {
// company: {
// com_name: "TOP SERVE 2"
// }
//})
.modify(
{
company:
{
com_name: TOP SERVE 2
}
}
)
.then(function (updated) {
if (updated)
console.log("Success.");
else
console.log("Nothing was updated.");
})
.catch(function (err) { console.log(err); });
Any idea how can I accomplish that?
Thanks
Alex
You where right to use Table.update or Collection.modify. They should never delete other properties than the ones specified. Can you paste a jsitor.com or jsfiddle repro of that and someone may help you pinpoint why the code doesn't work as expected.
Now that you are saying I realised that company and contact stores are created dynamically and editedRecords store has the indexes explicitly declared therefore when update company or contact store, since dexie doesn't see the indexes will overwrite. I haven't tested it yet but I suspect this is the behaviour.
See the print screen below:
Dexie stores overview
Basically I have json raw data from db and in the browser I create the stores and stores data based on it, see code below:
function createDexieTables(jsonData) { //jsonData - array, is the json from db
const stores = {};
const editedRecordsTable = 'editedRecords';
jsonData.forEach((jsonPackage) => {
for (table in jsonPackage) {
if (_.find(dexieDB.tables, { 'name': table }) == undefined) {
stores[table] = 'dexieKey';
}
}
});
stores[editedRecordsTable] = 'dexieKey, table';
addDataToDexie(stores, jsonData);
}
function addDataToDexie(stores, jsonData) {
dbv1 = dexieDB.version(1);
if (jsonData.length > 0) {
dbv1.stores(stores);
jsonData.forEach((jsonPackage) => {
for (table in jsonPackage) {
jsonPackage[table].forEach((tableRow) => {
dexieDB.table(table).add(tableRow)
.then(function () {
console.log(tableRow, ' added to dexie db.');
})
.catch(function () {
console.log(tableRow, ' already exists.');
});
});
}
});
}
}
This is the json, which I convert to object and save to dexie in the value column and the key si "dexieKey":
[
{
"company": [
{
"dexieKey": "{1}",
"company": {
"com_pk": 1,
"com_name": "CloudFire",
"com_city": "Round Rock",
"serverLastEdit": [
{
"com_pk": "2021-06-02T11:30:24.774Z"
},
{
"com_name": "2021-06-02T11:30:24.774Z"
},
{
"com_city": "2021-06-02T11:30:24.774Z"
}
],
"userLastEdit": []
}
}
]
}
]
Any idea why indexes were not populated when generating them dynamically?
Given the JSON data, i understand what's going wrong.
Instead of passing the following to update():
{
company:
{
com_name: "TOP SERVE 2"
}
}
You probably meant to pass this:
{
"company.com_name": "TOP SERVE 2"
}
Another hint is to do the add within an rw transaction, or even better if you can use bulkAdd() instead to optimize the performance.

Dynamically create pages with Gatsby based on many Contentful references

I am currently using Gatsby's collection routes API to create pages for a simple blog with data coming from Contentful.
For example, creating a page for each blogpost category :
-- src/pages/categories/{contentfulBlogPost.category}.js
export const query = graphql`
query categoriesQuery($category: String = "") {
allContentfulBlogPost(filter: { category: { eq: $category } }) {
edges {
node {
title
category
description {
description
}
...
}
}
}
}
...
[React component mapping all blogposts from each category in a list]
...
This is working fine.
But now I would like to have multiple categories per blogpost, so I switched to Contentful's references, many content-type, which allows to have multiple entries for a field :
Now the result of my graphQL query on field category2 is an array of different categories for each blogpost :
Query :
query categoriesQuery {
allContentfulBlogPost {
edges {
node {
category2 {
id
name
slug
}
}
}
}
}
Output :
{
"data": {
"allContentfulBlogPost": {
"edges": [
{
"node": {
"category2": [
{
"id": "75b89e48-a8c9-54fd-9742-cdf70c416b0e",
"name": "Test",
"slug": "test"
},
{
"id": "568r9e48-t1i8-sx4t8-9742-cdf70c4ed789vtu",
"name": "Test2",
"slug": "test-2"
}
]
}
},
{
"node": {
"category2": [
{
"id": "75b89e48-a8c9-54fd-9742-cdf70c416b0e",
"name": "Test",
"slug": "test"
}
]
}
},
...
Now that categories are inside an array, I don't know how to :
write a query variable to filter categories names ;
use the slug field as a route to dynamically create the page.
For blogposts authors I was doing :
query authorsQuery($author__slug: String = "") {
allContentfulBlogPost(filter: { author: { slug: { eq: $author__slug } } }) {
edges {
node {
id
author {
slug
name
}
...
}
...
}
And creating pages with src/pages/authors/{contentfulBlogPost.author__slug}.js
I guess I'll have to use the createPages API instead.
You can achieve the result using the Filesystem API, something like this may work:
src/pages/category/{contentfulBlogPost.category2__name}.js
In this case, it seems that this approach may lead to some caveats, since you may potentially create duplicated pages with the same URL (slug) because the posts can contain multiple and repeated categories.
However, I think it's more succinct to use the createPages API as you said, keeping in mind that you will need to treat the categories to avoid duplicities because they are in a one-to-many relationship.
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const result = await graphql(`
query {
allContentfulBlogPost {
edges {
node {
category2 {
id
name
slug
}
}
}
}
}
`)
let categories= { slugs: [], names: [] };
result.data.allContentfulBlogPost.edges.map(({node}))=> {
let { name, slug } = node.category2;
// make some checks if needed here
categories.slugs.push(slug);
categories.names.push(name);
return new Set(categories.slugs) && new Set(categories.names);
});
categories.slugs.forEach((category, index) => {
let name = categories.names[index];
createPage({
path: `category/${category}`,
component: path.resolve(`./src/templates/your-category-template.js`),
context: {
name
}
});
});
}
The code's quite self-explanatory. Basically you are defining an empty object (categories) that contains two arrays, slugs and names:
let categories= { slugs: [], names: [] };
After that, you only need to loop through the result of the query (result) and push the field values (name, slug, and others if needed) to the previous array, making the needed checks if you want (to avoid pushing empty values, or that matches some regular expression, etc) and return a new Set to remove the duplicates.
Then, you only need to loop through the slugs to create pages using createPage API and pass the needed data via context:
context: {
name
}
Because of redundancy, this is the same than doing:
context: {
name: name
}
So, in your template, you will get the name in pageContext props. Replace it with the slug if needed, depending on your situation and your use case, the approach is exactly the same.

fetching additional information for a particular list item in relay/graphql

Using Relay and GraphQL, let's say that I have a schema that returns a viewer, and an embedded list of associated documents. The root query (composed with fragments) would look like something like this:
query Root {
viewer {
id,
name,
groups {
edges {
node {
id,
name,
}
}
}
}
}
This will allow me to display the user, and a list of all of its associated groups.
Now let's say that I want the user to be able to click on that list item, and have it expand to show the comments associated with that particular list item. How should I restructure my query for the relay route such that I can receive those comments? If I add a comments edge to my groups edge, then won't it fetch the comments for all of the groups?
query Root {
viewer {
id,
name,
groups {
edges {
node {
id,
name,
comments {
edges {
node {
id,
content
}
}
}
}
}
}
}
}
Or should I alter the route query to find a specific group?
query Root {
group(id: "someid"){
id,
name,
comments {
edges {
node {
id,
content
}
}
}
},
viewer {
id,
name,
groups {
edges {
node {
id,
name,
}
}
}
}
}
My concern is, in particular, using this within the context of relay. I.e., how can I efficiently construct a route query that will only fetch the comments for the expanded list item (or items), while still taking advantage of the cached data that already exists, and will be updated when doing mutations? The above example might work for a specific expanded group, but I'm not sure how I could expand multiple groups simultaneously without fetching those fields for all of the group items.
Relay 0.3.2 will support the #skip and #include directives.
Group = Relay.createContainer(Group, {
initialVariables: {
numCommentsToShow: 10,
showComments: false,
},
fragments: {
group: () => Relay.QL`
fragment on Group {
comments(first: $numCommentsToShow) #include(if: $showComments) {
edges {
node {
content,
id,
},
},
},
id,
name,
}
`,
},
});
In your render method, only render comments if this.props.group.comments is defined. Invoke this.props.relay.setVariables({showComments: true}) in the Group component to cause the comments field to be included (and fetched, if necessary).
class Group extends React.Component {
_handleShowCommentsClick() {
this.props.relay.setVariables({showComments: true});
}
renderComments() {
return this.props.group.comments
? <Comments comments={this.props.group.comments} />
: <button onClick={this._handleShowCommentsClick}>Show comments</button>;
}
render() {
return (
<div>
...
{this.renderComments()}
</div>
);
}
}

jsPlumb connecting custom overlays - endpoint not moved

I am creating custom overlays for my connections in jsPlumb.
connectorOverlays: [
["Custom", {
create: function(component) {
return connectionNode();
},
location:0.5,
id:"customOverlay"
}]
]
connectionNode() creates a div with a select list in it. The divs themselves should be connectable, i.e., I want to connect two labels of different connectors.
var overlay_div = $(connection.overlays[0].canvas);
jsPlumb.addEndpoint(overlay_div, { anchor:[ "Perimeter", { shape:"Rectangle"}] }, relationEndpoint);
Enpoint on the connector overlay is defined as such:
var relationEndpoint = {
endpoint:["Dot", {radius:4} ],
anchor: ["BottomRight","TopLeft", "TopRight", "BottomLeft"],
paintStyle:{ fillStyle:"#ff33333", opacity:0.5 },
isSource:true,
connectorStyle:{ strokeStyle:"#ff33333", lineWidth:3 },
connector : "Flowchart",
isTarget:true,
dropOptions : {
tolerance:"touch",
hoverClass:"dropHover",
activeClass:"dragActive"
},
beforeDetach:function(conn) {
return confirm("Detach connection?");
}
};
When I now move any of the original nodes that are connected by the connection with the custom overlay, the endpoint is not moved along with the label but remains static where it was created. Can I somehow trigger its movement programatically or am I missing some configuration option?
I had this problem too. I was able to fix it with
instance.draggable(jsPlumb.getSelector(".flowchart-demo .window"), { grid: [5, 5] });
where ".flowchart-demo .window" is the class of the div.
The example shown is from jplumb page examples... in my project this line stays
instance.draggable(jsPlumb.getSelector(".flowchart-demo .clsExecutando"), { grid: [5, 5] });
I hope this information helps you.

Resources