Access/Display contentid in the view of a model? - episerver

I have created a new block type in episerver, and I am working on editing it's corresponding view. I would like to be able to display the contentid of the block in my html so that I can work some magic using javascript.
How can I get the contentid of the block from within it's view?
I would like to be able to do this:
<div class="something" data-id="#Model.contentid"></div>

You need to cast the block instance to IContent and then use #Model.ContentLink.ID.
So, something like:
<div class="something" data-id="#(((IContent)Model).ContentLink.ID)"></div>
Reason for the cast is that the IContent interface is only implemented by the proxy class for your block type, which is why you won't find the ContentLink property on a BlockData instance directly.
If you're interested in the details: https://world.episerver.com/Blogs/Johan-Bjornfot/Dates1/2012/11/Shared-blocks--IContent/

Related

How can I detect the state of Content at View startup?

Lets assume I have created my own custom view for a Link content type. When the user adds a 2sxc Content app to a Pane, then picks the Content Type (Link) then my custom View, when it first starts up, how can I detect that a) the View does not use a Demo item vs. b) the View uses a demo item and is showing the Demo item vs. c) its not the first time and there is a real user added Content (Entity) in place?
I have done stuff like this for the a) case:
var link = AsDynamic(Data["Default"]).First();
then checked if it was null, but it looks like my View code never executes and instead I just see, "No demo item exists for the selected template."
If I do assign a demo, is there a more elegant way to know that the Entity I am handed as Content.First() or Data["Default"]).First() is a Demo item and now a user created Entity? Currently I am hard-coding the EntityId in the template and testing for that.
The template system does not render the template if there is no demo item (unless it's a template without a content-type).
When we need this, we have two ways
give the demo item a unique value in one of the fields and check for that in the template
check the demo-item ID on GUID and check for that (Content.EntityGuid == ...)
IsDemoItem property added in 2sxc 10.06
Dynamic Entity
If a Content Editor "Hides" the only Content Item, the anonymous user will then see a Demo Item where the item was. This is confusing and unexpected from the Content Editor's point of view (as well as the public/anonymous user). If anyone else runs in to it, here is the simple code snippet to add to the start of your view. Basically, if the current user is not logged in and the item to display is a demo item, exit the View w/o displaying anything.
if(!Request.IsAuthenticated) {
if(Content.IsDemoItem ?? false) {
return;
}
}
Best to put it near the start of your first #{} Razor block.
Note: this will not throw an error in 2sxc prior to 10.6.x (because of the "?? false"), but it will not work either.

Defining Heap Analytics events with components build by libraries?

I'm using Heap analytics to capture events. What's frustrating is that I'm using styled-components and and-design, so events will be defined like so:
As a workaround, I'm adding IDs (or data attributes) to my components like this:
However, this is where my problem lies: I'm using a component library, Ant design and I'm trying to add an ID or data attribute to a Select component:
import Select from 'antd/lib/select'
<div id="headcount--dropdown">
<Select>
{props.options}
</Select>
</div>
However when I look at the event, I see something like this:
Where the auto generated selector should be #headcount--dropdown
I'm not sure how to get the auto generated selector to ONLY show my ID or data attributes. Could someone please help?

How do I get this value from my html in Typescript?

I have this element on my html page:
<div class="section-title" id="bladeSectionTitle"
ng-transclude="title">
</div>
I want to get the value displayed.
I have tried the following in my typescript page & only get null:
var title = document.getElementById("bladeSectionTitle").getAttribute('section-title');
The view source gives me this:
<dpn-blade-section is-checkbox-visible="true" is-checked="$component.showAll">
<section-title>
<h4>Show All</h4>
</section-title>
In this instance, the value I would be looking for is 'Show All'.
Which version of angular are you running? We really need more information here. Although accessing the DOM directly isn't the way to go with angular, you're looking at something like this
var title = document.querySelector('dpn-blade-section section-title h4').innerHTML;

StructBlock within StreamField is rendered as text

I have an issue where I have a StrictBlock within a StreamField:
class DetailsTableBlock(StructBlock):
title = CharBlock(required=False)
description = RichTextBlock(required=False)
table = TableBlock(template="home/blocks/table.html")
class MainStreamBlock(StreamBlock):
....
table = DetailsTableBlock()
The problem occurs when I try to render the table using:
{{ child.value.table }}
all I get is:
{u'data': [[u'test', u'test', u'test'], [u'123', u'asd', u'asd'], [u'123', u'asd', u'asd']], u'first_row_is_table_header': True, u'first_col_is_header': False}
So the question is how can I render html using a StructBlock inside a StreamField? I am using Wagtail 1.7
You should use: {{ child.value.bound_blocks.table }}
The full explanation is given at wagtail documentation, but in short: when you're looping over the content of a StreamField to output it, you'll sometimes get the raw data value, and sometimes get a BoundBlock object which knows both the value and how to render it as HTML. When you access the child values of a StructBlock, you get the raw values (since this is usually the more useful thing to access inside a StructBlock template) - to get the BoundBlock object, you need to read it from the StructBlock's bound_blocks dictionary instead.

How should I integrate my directive and scope object in the view?

In my controller I have an array of objects. The object is called Well has a few properties, one of which is Location, which stores a string like "A1", "B4", "B13", etc. The location indicates a position on a grid. The letter represents the row, and the number represents the column.
Now that I have this nice list of objects, I would like to display them all on a grid in my view. When I say grid, I mean that loosely. The grid I have come up with is a series of divs, each with an id equal to a location name.
I have created a directive called tile that will display the properties of a single object. The directive looks like so:
<div class="row">
<div class="col-md-3" ng-repeat="well in wellArray">
<ul><li ng-repeat="prop in well">{{ prop }}</li></ul>
</div>
</div>
Great! And then I can create a tile in my view for a specific Well in the list of Well objects like so:
<div tile name="{{my.getName()}}" dil="{{my.getDilution()}}"></div>
If this list of objects was ordered by the location property, I could simply turn it into an array of arrays, one array for each row, and then use a double ng-repeat in my view. Unfortunately they are not in order, and I do not want to create a sorting method given the format that the location property is in. If i were to do the double ng-repeat on this list as it is now, I would end up with a grid of tiles that are in no particular order.
Given my limited exposure to javascript, I thought of using jquery's .append() method.(note: i have referenced jquery before angular, so angular.element() will use the jquery library instead of jqlite so I can use jquery selectors) In my view I created a bunch of divs in the following format:
<div id="A1"></div>
<div id="A2"></div>
etc.
And then in my controller I created a method that attempts to append a single Well which has a location of "A1" to the element on the view that has an id="A1". My code looks like so:
angular.element('#A1').append('<div tile name="{{my.getName()}}" dil ="{{my.getDilution()}}"></div>');
I thought it would append the div with the tile directive, to the div with id="A1", however, it does nothing. In fact, there are no errors at all.
Surely my psuedo jquery approach is not the best way to go about this. Not only is it not working (no idea why, maybe because angular needs to compile something somehow), but it's also not a very Angular approach. I keep reading in tutorials and introductions to "not use jquery at all for the first few weeks" and "90% of the things you'll waste lines of code in jquery, can be done suceinctly in Angular". Someone please lend this poor excuse of a programmer a hand!!
Just following your example in comments with .append, instead of iterating over your array and appending elements to a container element, create a conceptual representation of the data, and then use it in the view.
In controller, do something like the following:
$scope.wellData = {};
for (well in wellArray){
var key = wellArray[well].getLocation();
$scope.wellData[key] = well;
}
Then in the view, do ng-repeat over wellData:
<div id="item.getLocation()" ng-repeat="item in wellData">
<div tile name="{{item .getName()}}" dil="{{item .getDilution()}}"></div>
</div>
You definitely should stay away from jQuery in controllers. Just assume that there is no DOM in controllers whenever you get the urge to do anything related to DOM. Controller deals with ViewModels which are conceptual representation of the view, but it is view-independent. Whenever you break that separation, you make your controllers harder to test, and you make your view more difficult to change. And, by going against MVVM principles, you will keep bumping into issues with AngularJS.

Resources