I have a vaadin grid in which I have information in. I would like the grid to instead of open a dialog, to route to a new page just like any other link would do.
I am using app-route, but am not understanding how to get to the next page.
Each row has its own ID which is all that should be passed in the routing.
I would like each row to have its own link when clicked too. So each id should be localhost:8000/dashboard/{{id}} by calling the same element that builds the page depending on the id passed.
Is there a way to do this out of the vaadin grid?
One way to do that with <vaadin-grid> would be to observe the activeItem property of the grid, and update the URL every time it changes:
1. observe the activeItem grid property
<vaadin-grid items="[[pages]]" aria-label="Navigation Links"
on-active-item-changed="_onActiveItemChanged">
<vaadin-grid-column>
<template class="header">Grid Menu</template>
<template>[[item.title]]</template>
</vaadin-grid-column>
</vaadin-grid>
2. define a new path based on the selected grid item
_onActiveItemChanged(e, detail) {
const activeItem = detail.value;
if (activeItem) {
// if a grid item is selected
this.path = `/dashboard/${activeItem.id}`;
}
}
3. update the current URL based on the path
(for client-side routing) <app-location path="[[path]]"></app-location>.
(for server-side routing) window.location.pathname = this.path;.
This example shows <vaadin-grid> 4, but the same principle applies to any other version of the grid as well.
Related
When elements created dynamically using data binding (including those in dom-repeat and dom-if templates), iron-grid component doesn't toggle responsive classes.
You must call onResize event after that elements created, like this:
var grid = this.$.grid;
grid.currentScreenformat = null;
grid.onResize();
I have run into a problem where paper-dropdown element's on-core-select event is being fired by a core-selector belonging to a separate element in my polymer app. Here are excerpts from the polymer element that includes the paper-dropdown (along with the polymer event script):
<paper-dropdown id="widthUnits" class="unitSelection" selected="{{item.data.designWidth[1]}}" on-core-select="{{ conditionUnitSelectionChanged }}" valueattr="label">
<paper-item label="mm"></paper-item>
<paper-item label="cm"></paper-item>
<paper-item label="m"></paper-item>
</paper-dropdown>
conditionUnitSelectionChanged: function(e, detail, sender) {
// Ensure the selection has in fact changed
if (sender.selected != detail.item.label)
{
this.ajaxUpdateUnit(sender);
}
},
And here is the core-selector and related code that is part of an entirely different element within the application. FYI, SelectedItemKey is watched by polymer 'Changed' events in both of the elements involved...if that matters.
<core-selector id="itemSelector" target="{{$.itemList}}" multi="false" selected="{{selectedItemKey}}" selectedAttribute="active"></core-selector>
<div id="itemList" layout horizontal wrap>
<template repeat="{{item, i in items}}">
<div id="{{item.name}}">
<entriesHeading name="{{item.name}}" horizontal layout center>
<div flex>{{item.name}}</div>
<paper-menu-button icon="more-vert" halign="right">
<paper-item label="Edit" on-tap="{{ itemEdit }}"></paper-item>
<paper-item label="Copy" on-tap="{{ itemCopy }}"></paper-item>
<paper-item label="Delete" on-tap="{{ itemDelete }}"></paper-item>
</paper-menu-button>
</entriesHeading>
<entriesContainer vertical layout>
*** container contents ***
</entriesContainer>
</div>
</template>
</div>
Any suggestions on how I can avoid this unwanted interplay with core-select events? Perhaps a specific listener of some sort (limited to listening for paper-dropdown(s) core-select event)?
It's not possible for the paper-dropdown to receive an event from anywhere but inside it's own subtree. You have to present a jsbin or some kind of reproduction, otherwise I must suggest your diagnosis is incorrect.
You should try to figure out what is going on with the events, to make sure you have good understanding of the system.
Having said that, another way of approaching the problem is by being data-driven and not control-driven.
IOW, it's best to react to data-changes instead of events. It's hard to give really good suggestions because I can only see a tiny piece of your application, but here are some suggestions:
You have
<paper-dropdown id="widthUnits" class="unitSelection"
selected="{{item.data.designWidth[1]}}"
on-core-select="{{ conditionUnitSelectionChanged }}" valueattr="label">
It's a bit unfortunate that this important data is referenced as item.data.designWidth[1]. Generally one wants to factor the application data so that you aren't using deeply nested expressions like that. Just as an example, if you could build a UI like design-width-editor and bind it to <design-width-editor designWidth="{{item.data.designWidth[1]}}"> then you could put logic inside of design-width-editor that just deals with designWidth and doesn't need to know about item or data. This gives you a lot more flexibility with your data structures and makes it easier to think about.
In any case, given the construction you have, one thing you could do is observe the data directly:
observe: {
'item.data.designWidth[1]`: 'designWidth1Changed'
}
Now you can implement designWidth1Changed() to take the needed action. The key bit here is that you are no longer dependent on any particular UI for modifying the designWidth data. You can replace that UI at will; all that matters is that if the value changes, some action is taken.
Scott put me on the right track. After some refactoring as described in the previous comments, I used async to my advantage in order to avoid observers executing when I didn't want them to (such as when the elements model item object changed...and therefore all of its observed properties). Here is a sample of some of the script code from the host element mentioned above that was implemented to resolve the final issue:
ignoreChanges: null,
observe: {
'item.data.designWidth[0]': 'designWidthValueChanged',
'item.data.designWidth[1]': 'designWidthUnitChanged',
}
designWidthValueChanged: function(oldVal, newVal) {
if (!this.ignoreChanges) {
// send update of width value input via ajax
this.ajaxUpdateCondition("designWidth", newVal, this.item.data.designWidth[1]);
}
},
designWidthUnitChanged: function(oldVal, newVal) {
if (!this.ignoreChanges) {
// send update of width unit selection via ajax
this.ajaxUpdateCondition("designWidth", this.item.data.designWidth[0], newVal);
}
},
itemKeyChanged: function(oldVal, newVal) {
// itemKey is a published attribute that is 2 way bound to a parent element (where item selection occurs from a collection)
this.toggleIgnoreChanges(true); //set flag to ignore changes to observed values while item object switches
this.item = this.items[newVal]; //point to the correct/selected item in the collection (items is published attribute of this element)
this.async(this.toggleIgnoreChanges); //reset flag after observe functions have executed
},
toggleIgnoreChanges: function(flagstatus) {
this.ignoreChanges = flagstatus || !this.ignoreChanges;
}
I am trying to make a polymer element that authenticates users against an SMF forum. I want the element to expose a "user" property which will initially be an empty object {} but if/when the element has made an ajax request and is able to authenticate the user, this object will contain details about the user (and an authentication token) for use in the rest of the application. The rest of the application is in the content section of the element, and will only be displayed when authetication has happened.
So the application is structured like this
<smf-auth login="login/url", fail="fail/url", splash="initial/splash/img" user>
rest of application which will need access to user
</smf-auth>
I have published user with reflective properties, and set its initial value to {}. However when I run unit tests, I run javascript to get the element (as a javascript variable) and look at el.user and it has the value "" (ie blank string). I tried altering the code to initialise user as something more complex, but I still get a blank string.
Here is the element definition (with my more complex user)
<polymer-element name="smf-auth" attributes="login fail splash">
<template>
<core-ajax id="req" url="{{login}}" handleAs="json" on-core-response="{{handleResponse}}"></core-ajax>
<img id="splash" src="{{splash}}"/>
<template if="{{autheticated}}">
<content></content>
</template>
</template>
<script>
Polymer('smf-auth',{
publish:{
user:{
value:{token:{},uid:0,name:''},
reflect:true
}
},
created:function(){
this.authenticated = false;
},
attached:function(){
this.$.req.go();
},
handleResponse:function(response){
//TODO decide what the response is
}
});
</script>
</polymer-element>
So how should I declare and use the "user" property so that the content of the element (more polymer elements) has access to it
You need to specify a data binding to a user property when you instantiate the smf-auth element. Then you can access this property inside the element's body:
<smf-auth user="{{user}}" ...>
Hello {{user.name}}!
</smf-auth>
This assumes that your smf-auth element is itself inside a Polymer element. Otherwise you need an auto-binding template element.
If your element hierarchy is deep and you need the user object in an element at the bottom, it can be a problem to pass the user property down the hierarchy. In this case another option might be to use core-signals and send an event when the user has logged in. Inner elements can then listen for this event and fetch the user object from the event details.
Introduce the Problem
I would like to profoundly modify the layout of the Orchard CMS Tags list.
Here is an example page with Shape Tracing enabled.
The only alternate that it suggests for the List shape is ~/Themes/TheThemeMachine/Views/List.cshtml, because the page is rendering the default List shape. I would like to have other alternates that are specific to the page.
After reading Orchard list customization, I have been able to implement the default List.cshtml in razor. What I would like to do, though, is to add another alternate, such as ~/Themes/TheThemeMachine/Views/Parts.Tags.List.cshtml instead of implementing the default List.cshtml template.
The problem seems to be that the page is rendering the generic List shape.
In contrast, the blog post list page is rendering a Parts_Blogs_BlogPost_List shape, which means that a ~/Themes/TheThemeMachine/Views/Parts.Blogs.BlogPost.List.cshtml is available.
Search and Research
All quotes below are from the Orchard list customization blog post, which explains how to add a list item alternate (whereas I would like to add a list alternate).
What we really want is an alternate template... aptly called Shape
Alternates... [so] enable Shape Tracing... and select a post in the list...
[you will see that] we already have some possible alternates.
My example page also has some possible alternates for the List Content. Cool.
we need to somehow get into list rendering... [t]he default is defined
in code... [which] can be override by a new [cshtml] template in our
theme.
Okay. That makes sense. We can override the list rendering.
As Shape Tracing can show, we can override the list rendering for a
blog by creating a Parts.Blog.BlogPost.List.cshtml template.
This works for alog but not for the blog Tag page (example page). You see, the blog displays a **Parts_Blogs_BlogPost_List shape and suggests an appropriate alternate but the blog tags page displays the default List shape with no alternates other than List.cshtml.
Blog Page with alternates galore
Blog Tags Page with one alternate List.cshtml
So, I created a List.cshtml not a Parts.Blog.BlogPost.List.cshtml template, and save it in my theme's Views directory. (One problem here is that, once we get it working, we will b overriding the default List rendering.)
Then I add the Razor code (copy and pasted from Bertrand's post) to override the default rendering for Lists. When I refresh the site, the browser renders a blank page. It isn't working. Here's the code:
This Does NOT Work in List.cshtml
#using Orchard.DisplayManagement.Shapes;
#{
var list = Model.ContentItems;
var items = list.Items;
var count = items.Count;
var listTag = Tag(list, "ul");
listTag.AddCssClass("content-items");
listTag.AddCssClass("blog-posts");
var index = 0;
}
#listTag.StartElement
#foreach (var item in items) {
var itemTag = Tag(item, "li");
if (index == 0) {
itemTag.AddCssClass("first");
}
else if (index == count - 1) {
itemTag.AddCssClass("last");
}
#itemTag.StartElement
#Display(item)
#itemTag.EndElement
++index;
}
#listTag.EndElement
As a trouble shooting step, I replace the List.cshtml with <p>Hello world.</p>. Orchard renders the markup as expected. So, something is incompatible between the Razor code from Bertrand's blog and the Tags List.
To find out what exactly is incompatible, I try Betrand's code one line at time to see where it breaks (yup, VS would be better than WM here). At each change, I restart WebMatrix and view the results. This is the minimal code that breaks it.
The Culprit
#using Orchard.DisplayManagement.Shapes;
#{
var list = Model.ContentItems;
var items = list.Items;
}
list.Items isn't appropriate here. So I comment it out again and run the <p>Hello World</p> version again. Also, Shape Tracing reveals that on my Tags/tagname page, the Content Zone is now rendering the List twice. Is that normal?
As another step, I replace Model.ContentItems just with Model. It works. It seems that, to override the List.cshtml template, we cannot use the ContentItems property of Model. Here is the new, working code:
This Does Work in List.cshtml
#using Orchard.DisplayManagement.Shapes;
#{
//var list = Model.ContentItems;
//var items = list.Items;
var items = Model.Items;
var count = items.Count;
//var listTag = Tag(list, "ul");
var listTag = Tag(Model, "ul");
listTag.AddCssClass("content-items");
listTag.AddCssClass("blog-posts");
var index = 0;
}
#listTag.StartElement
#foreach (var item in items) {
var itemTag = Tag(item, "li");
if (index == 0) {
itemTag.AddCssClass("first");
}
else if (index == count - 1) {
itemTag.AddCssClass("last");
}
#itemTag.StartElement
#Display(item)
#itemTag.EndElement
++index;
}
#listTag.EndElement
Onward through the article.
So far so good, we have effectively taken over the rendering of the
list, but the actual HTML [will] be... identical to what we had before
[except for] the implementation.
Okay. I'm following. We want to modify the rendering not just re-implement it.
Alternates are a collection of strings that describe additional shape
names for the current shape... in the Metadata.Alternates property of any shape.
Gotcha. Now, why doesn't the Tags/tagname page show an alternate other than just List.cshtml for the rendering of the List shape?
All we need to do is add to this list [of alternates]... [and make sure] to respect the lifecycle...
Great. Maybe we can we add another alternate for the List shape on the Tags/tagname page. But, doing that is different from what Betrand is explaining. While Betrand's blog post is excellent, it is explaining how to add an alternate for an item, whereas I would like to add an alternate for the list.
The List.cshtml template is where I would add an alternate for a List Item as follows:
ShapeMetadata metadata = item.Metadata;
string alternate = metadata.Type + "_" +
metadata.DisplayType + "__" +
item.ContentItem.ContentType +
"_First";
metadata.OnDisplaying(ctx => {
metadata.Alternates.Add(alternate);
});
So that...
[t]he list of alternates from Shape Tracing now contains a new item.
Where and how, though, would I add an alternate for the List shape? Bertrand has recommended to check out the Shape Table Providers blog post for this. The quotes below are from that post.
But what if you want to change another shape template for specific
pages, for example the main Content shape on the home page?
This looks like a fit, because my example is the main List shape on the tags page. To do this we...
... handle an event that is triggered every time a shape named "Content"
[in our case "List"] is about to be displayed. [It] is implemented in a shape table provider which is where you do all shape related site-wide operations.
Great! Here is my implementation for adding another template for the main List shape.
TheThemeMachine > ListShapeProvider.cs
namespace Themes.TheThemeMachine
{
using Orchard.DisplayManagement.Descriptors;
public class ListShapeProvider : IShapeTableProvider
{
public void Discover(ShapeTableBuilder builder)
{
System.Diagnostics.Debugger.Break(); // break not hit
builder.Describe("List").OnDisplaying(displaying => {
// do stuff to the shape
displaying.ShapeMetadata.Alternates.Add("Tags__List");
});
}
}
}
The above builds and runs but does not hit the breakpoint nor add an alternate for the List shape on the /tags page. So I looked into the Orchard.Azure.MediaServices module and its CloudVideoPlayerShape which implements IShapeTableProvider. Its breakpoint does get hit. How is my code for ListShapeProvider fundamentally different than the code for the CloudVideoPlayerShape?
Also, I installed the Orchard.Themes.CustomLayoutMachine.1.0.nupkg as suggested in Bertrand's blog post. It unfortunately no longer contains an implementation of IShapeTableProvider.
I have also looked at this szmyd post, which does not explain where to put the IShapeTableProvider code.
Further, I installed the Contoso theme from the Orchard Gallery. It works and builds after adding a reference to Microsoft.CSharp. It also includes an implementation of the IShapeTableProvider. Hooray! Comparing its ContentShapeProvider with my ListShapeProvider reveals a subtle but important difference:
Contoso.csproj
<ItemGroup>
<Compile Include="Code\ContentShapeProvider.cs" />
</ItemGroup>
My implementation didn't include the .cs file in the compilation, because my theme has neither a .csproj nor a App_Code folder. So, I recreated my theme with the following code generation:
orchard.exe
feature enable Orchard.CodeGeneration
codegen theme My.FirstTheme /CreateProject:true
theme enable My.FirstTheme
feature enable Orchard.DesignerTools
When adding the ListShapeProvider.cs file, Visual Studio automatically added a ItemGroup/Compile entry for the file, which included the code in compilation. Hooray!
These two posts will help.
Shape Shifting
List Customization
Here are steps of my own minimum solution.
Download and unzip Orchard.Source.1.8.zip.
Open "\Downloads\Orchard.Source.1.8\src\Orchard.sln" in Visual Studio.
Build the solution to create orchard.exe.
Generate a new theme with orchard.exe. Use CreateProject:true because you will need a csproj to include your .cs file.
orchard.exe
setup /SiteName:SITE /AdminUsername:ME /AdminPassword:PWD /DatabaseProvider:SqlCe
feature enable Orchard.CodeGeneration
codegen theme My.FirstTheme /CreateProject:true
theme enable My.FirstTheme
In VS, add a ListShapeProvier.cs file to the root (or any folder) in your theme.
Add the following code to ListShapeProvider.cs.
namespace My.FirstTheme
{
using Orchard.DisplayManagement.Descriptors;
public class ListShapeProvider : IShapeTableProvider
{
public void Discover(ShapeTableBuilder builder)
{
System.Diagnostics.Debugger.Break();
// implementation here
}
}
}
Build the solution.
Run Orchard.Web.
Visual Studio will break at System.Diagnostics.Debugger.Break(). If it doesn't, go to the Orchard Dashboard and make My.FirstTheme the Current Theme.
Now read Shape Shifting to implement public void Discover(ShapeTableBuilder builder).
This post should give you a full response: http://weblogs.asp.net/bleroy/archive/2011/05/23/orchard-list-customization-first-item-template.aspx
I have a 6 items of the same content type "news", in each item I have a field newsIntro. I want to put the fields in specific pages on another page so I need to target a specific field so it may be newsIntro on node 1702. I have tried a few things like
#1720.newsIntro
how do I target a specific field
Thanks
There are some great resources you should take a look at while you are learning Razor:
Umbraco Razor Feature Walkthrough - An eight part blog post series of many of the new Razor features in Umbraco 4.7 with examples.
Razor DynamicNode Cheat Sheet - A PDF of all the properties and methods available to the Razor DynamicNode object (that includes #Model).
Cultiv Razor Examples - An Umbraco website that you can download and open with WebMatrix or IIS and see various ways to access properties with Razor.
Razor snippets - A compilation of different snippets, examples, etc. from Our Umbraco.
But in answer to your question, to get a property of a specific node you have to get the actual DynamicNode object first, then use the property alias to access the property value. Example:
#{
//Get the node
dynamic node = Library.NodeById(1720);
// Display the property
#node.newsIntro
}
To access a property from the current page, you simply use Model:
#Model.newsIntro
or
#Model.bodyText
or
#Model.Name
First, get an IPublishedContent object from the TypedContent method and then use GetPropertyValue to retrieve the value of the field.
#{
int nodeId = 1720;
IPublishedContent contentNode = Umbraco.TypedContent(nodeId);
var newsIntro = contentNode.GetPropertyValue("newsIntro");
}
<p>#newsIntro</p>
With Umbraco 7 I used this code to get property from different pages:
#Umbraco.Content(1720).newsIntro
If the content item 1720 is a parent or ancestor of the page where you want to use the value, you can get it recursively like this:
#Umbraco.Field("newsIntro", recursive: true)
To get fields from content I have used this:
#{
var selection = Umbraco.TypedContent(contentId).Children()
.Where(x => x.IsVisible())
.OrderBy("CreateDate");
}
#foreach(var item in selection){
#item.GetPropertyValue("fieldName1")
#item.GetPropertyValue("fieldName2")
#item.GetPropertyValue("fieldName_N")
}