CSS class on generated div element for EPiServer blocks - asp.net-mvc

I have a ContentArea with a number of floating blocks. EPiServer automatically wraps each block in a div-element, which is necessary for the edit mode to function properly. So what is initially one div becomes three nested divs: content area, child element wrapper and the block view.
Is it possible to add CSS classes to the child element wrapper from the block view? So what is today:
div.ContentArea > div > div.my-class
becomes:
div.ContentArea > div.my-class

If you want to avoid the extra wrapping divs, take a look into this post
My guess is that this is what you should render:
#Html.PropertyFor(x => x.Teasers,
new
{
ChildrenCustomTagName ="div",
ChildrenCssClass = "my-class"
})
Don't render a wrapping div element in the partial view, only the "inner content" (since a wrapping div element will be rendered regardless for each item in the content area).
The wrapping element rendered when the content area is rendered cannot be excluded, as that would break the on-page editing features in EPiServer.
Hope this helps and is clear enough.

I ended up using a custom content area renderer:
public class CustomContentAreaRenderer : ContentAreaRenderer
{
protected override string GetContentAreaItemCssClass(HtmlHelper htmlHelper, ContentAreaItem contentAreaItem)
{
var tag = GetContentAreaItemTemplateTag(htmlHelper, contentAreaItem);
return string.Format("block {0} {1} {2}", GetTypeSpecificCssClasses(contentAreaItem, ContentRepository), "my own classes", tag);
}
}
I apply the custom renderer to the container with this code:
container.For<ContentAreaRenderer>().Use<CustomContentAreaRenderer>();
Thank you for your help!

Here you can read all there is about exending contentarea, and also why they work like they do.
http://blog.tech-fellow.net/2015/06/11/content-area-under-the-hood-part-3/

It's possible to completely remove the extra div's by overriding the default content area. I've written quite a detailed tutorial here EpiServer 7 : Extra divs in content area how to remove them ?
Going with this approach is quite extreme though. If you only have this issue in one or two places then I would recommend using something like this
#Html.PropertyFor(x => Model.MainContentArea, new
{
CustomTag = "ul",
CssClass = "list",
ChildrenCustomTagName = "li",
ChildrenCssClass = "list_item",
Tag = string.Empty
})
You can read more about how these properties work here : How To Render EpiServer Blocks In Your Views Using PropertyFor

Related

Access elements in Angular Dart

I have a top level element <x-app> with nested modal dialogs
<x-app>
<material-content>
...
</material-content>
<x-alert-dialog></x-alert-dialog>
</x-app>
where <x-alert-dialog> contains
<modal [visible]="dlgVisible" dialog-id="alert-dialog-modal">
<material-dialog class="alert-dialog">
....
</material-dialog>
</modal>
Generated HTML contains <x-app> and overlay container div which contains modals as on the image
What I need is to access <div pane-id="default-1"...> to change z-index. and I don't know how. I cannot access it in CSS as any reference via :host is not possible.
I tried to access it programatically in x-app component. I have
class AppComponent implements AfterViewInit {
#override
void ngAfterViewInit() {
var doc = getDocument();
var alertDlg = doc.querySelector(".alert-dialog");
var alertPane = alertDlg.parent;
}
}
But alertDlg is always null. I also tried var alertDlg = querySelector(".alert-dialog");
Is there any way to access the element?
I solved it by adding *ngIf="dlgVisible" to <modal> tag, so it's now
<modal *ngIf="dlgVisible" [visible]="dlgVisible" dialog-id="alert-dialog-modal">
This way the dialog is injected to/removed from DOM by the visibility flag. The reason why I wanted to change z-index was to get the alert and other app wide dialogs above other dialogs created later in other components.
Altering DOM solves this as the dialogs are inserted after (and thus displayed above) other dialogs. Hope this will help someone.

Event when an Component is added to the dom

Say I have a AngularDart component that adds a div and an iframe to that div as it's template.
I have the element passed for the outer component in the components constructor
#Component(
selector: "input-html",
templateUrl: "packages/myproject/components/inputs/html.html",
useShadowDom: false
)
class HtmlComponent implements ShadowRootAware {
HtmlComponent(NgModel ngModel, Element element):super(ngModel, element){
}
}
I have shadowdom turned off because I'm using Bootstrap for styling and want the elements easily accessible for the bootstrap css.
My template code is along the lines of
<div>
<iframe id="my-iframe"></iframe>
</div>
It's more complicated than that, there's a bunch of buttons etc, as I'm porting a javascript html editor to angulardart.
My problem is, I need to get the iframe element, but whenever I query element.querySelector("#my-iframe") or even window.document.querySelector("#my-iframe") the object is null. I believe this is because the template hasn't been added to the DOM yet.
I need the iframe object because I need to set the iframe content for the HTML editor to work. There's a few other areas of my project that I wanted to get the template dom objects but couldn't either.
I've tried onShadowRoot, which worked in AngularDart 0.14 but no longer works in 1.0. I've tried ScopeAware and querying for the iframe when the scope is set, but that didn't work (ScopeAware fires before shadowroot event).
I have a hack that's messy that works, by using ng-show="init()" and in that init method I have
bool _initDone = false;
bool init() {
if(_initDone == false) {
iframe = element.querySelector("#my-iframe")
_initDone = true;
}
return true;
}
Which works, but it's messy and I don't like that solution and obviously isn't the correct way to do it.
Anyone know how I can achieve this in AngularDart 1.0?
I think onShadowRoot is the right place for the code to query the element. If it really doesn't work wrap it in a Future to add it as a task at the end of the event queue to delay it a bit more.
onShadowRoot() {
new Future(() {
querySelector(...);
});
}

Rendering dynamic scss-files with ajax, rails

As the title suggests, my main objective is to render a dynamic scss(.erb) file after an ajax call.
assets/javascripts/header.js
// onChange of a checkbox, a database boolean field should be toggled via AJAX
$( document ).ready(function() {
$('input[class=collection_cb]').change(function() {
// get the id of the item
var collection_id = $(this).parent().attr("data-collection-id");
// show a loading animation
$("#coll-loading").removeClass("vhidden");
// AJAX call
$.ajax({
type : 'PUT',
url : "/collections/" + collection_id + "/toggle",
success : function() {
// removal of loading animation, a bit delayed, as it would be too fast otherwise
setTimeout(function() {
$("#coll_loading").addClass("vhidden");
}, 300);
},
});
});
});
controller/collections_controller.rb
def toggle
# safety measure to check if the user changes his collection
if current_user.id == Collection.find(params[:id]).user_id
collection = Collection.find(params[:id])
# toggle the collection
collection.toggle! :auto_add_item
else
# redirect the user to error page, alert page
end
render :nothing => true
end
All worked very smooth when I solely toggled the database object.
Now I wanted to add some extra spices and change the CSS of my 50+ li's accordingly to the currently selected collections of the user.
My desired CSS looks like this, it checks li elements if they belong to the collections and give them a border color if so.
ul#list > li[data-collections~='8'][data-collections~='2']
{
border-color: #ff2900;
}
I added this to my controller to generate the []-conditions:
def toggle
# .
# .
# toggle function
# return the currently selected collection ids in the [data-collections]-format
#active_collections = ""
c_ids = current_user.collections.where(:auto_add_item => true).pluck('collections.id')
if c_ids.size != 0
c_ids.each { |id| #active_collections += "[data-collections~='#{id}']" }
end
# this is what gets retrieved
# #active_collections => [data-collections~='8'][data-collections~='2']
end
now I need a way to put those brackets in a scss file that gets generated dynamically.
I tried adding:
respond_to do |format|
format.css
end
to my controller, having the file views/collections/toggle.css.erb
ul#list<%= raw active_collections %> > li<%= raw active_collections %> {
border-color: #ff2900;
}
It didn't work, another way was rendering the css file from my controller, and then passing it to a view as described by Manuel Meurer
Did I mess up with the file names? Like using css instead of scss? Do you have any ideas how I should proceed?
Thanks for your help!
Why dynamic CSS? - reasoning
I know that this should normally happen by adding classes via JavaScript. My reasoning to why I need a dynamic css is that when the user decides to change the selected collections, he does this very concentrated. Something like 4 calls in 3 seconds, then a 5 minutes pause, then 5 calls in 4 seconds. The JavaScript would simply take too long to loop through the 50+ li's after every call.
UPDATE
As it turns out, JavaScript was very fast at handling my "long" list... Thanks y'all for pointing out the errors in my thinking!
In my opinion, the problem you've got isn't to do with CSS; it's to do with how your system works
CSS is loaded static (from the http request), which means when the page is rendered, it will not update if you change the CSS files on the server
JS is client side and is designed to interact with rendered HTML elements (through the DOM). This means that JS by its nature is dynamic, and is why we can use it with technologies like Ajax to change parts of the page
Here's where I think your problem comes in...
Your JS call is not reloading the page, which means the CSS stays static. There is currently no way to reload the CSS and have them render without refreshing (sending an HTTP request). This means that any updating you do with JS will have to include per-loaded CSS
As per the comments to your OP, you should really look at updating the classes of your list elements. If you use something like this it should work instantaneously:
$('li').addClass('new');
Hope this helps?
If I understood your feature correctly, actually all you need can be realized by JavaScript simply, no need for any hack.
Let me organize your feature at first
Given an user visiting the page
When he checks a checkbox
He will see a loading sign which implies this is an interaction with server
When the loading sign stopped
He will see the row(or 'li") he checked has a border which implies his action has been accepted by server
Then comes the solution. For readability I will simplify your loading sign code into named functions instead of real code.
$(document).ready(function() {
$('input[class=collection_cb]').change(function() {
// Use a variable to store parent of current scope for using later
var $parent = $(this).parent();
// get the id of the item
var collection_id = $parent.attr("data-collection-id");
show_loading_sign();
// AJAX call
$.ajax({
type : 'PUT',
url : "/collections/" + collection_id + "/toggle",
success : function() {
// This is the effect you need.
$parent.addClass('green_color_border');
},
error: function() {
$parent.addClass('red_color_border');
},
complete: function() {
close_loading_sign(); /*Close the sign no matter success or error*/
}
});
});
});
Let me know if my understanding of feature is correct and if this could solve the problem.
What if, when the user toggles a collection selection, you use jquery change one class on the ul and then define static styles based on that?
For example, your original markup might be:
ul#list.no_selection
li.collection8.collection2
li.collection1
And your css would have, statically:
ul.collection1 li.collection1,
ul.collection2 li.collection2,
...
ul.collection8 li.collection8 {
border-color: #ff2900;
}
So by default, there wouldn't be a border. But if the user selects collection 8, your jquery would do:
$('ul#list').addClass('collection8')
and voila, border around the li that's in collection8-- without looping over all the lis in javascript and without loading a stylesheet dynamically.
What do you think, would this work in your case?

grails current page highlight

In grails I have a menu. I'm trying to highlight the menu item for the active page:
link1
link2 (show as bold if the controller action partially matches this gsp name)
link3
I have working code but it seems like there would be a better way.
Taglib works fine:
def active = { attrs, body ->
for (page in attrs.check) {
if (params.action.startsWith(page)) {
out << "current"
break
}
}
}
This option works fine, but seems wordy:
<li>Contact Info</li>
<li>About You</li>
This blows up:
<g:link action='myProfile' class="${<xyz:active check='${['myControllerAction']}'/>}">My Profile</g:link>
I don't believe you can pass a taglib as a parameter to g:link
I also have the requirement that multiple gsps/actions would cause a link to be active because of how they are named:
aboutYouLocation
aboutYouBackground
aboutYouEducation
all make this link the active one:
About You
I can do a partial match, but I've also got some actions/gsps that begin with aboutYour (extra R) resulting in my use of the array being passed into my taglib.
There's a standard way of doing that with the Platform Core plugin. It will provide you a Navigation API:
grails-app/conf/AppNavigation.groovy
navigation = {
// Declare the "app" scope, used by default in tags
app {
contact(action: 'contactInfo')
about(action: 'aboutYouFamily')
}
}
*grails-app/view/_menu.gsp* (template that you can use in your layout or GSP's)
<nav:menu scope="app" id="navigation" />
You can also customize the html generated for your menu, check the custom item rendering.

jQuery UI dialog display inside frame, from bookmarklet?

I'm writing a bookmarklet which needs to work in the context of pages whose design I don't control. Some of the pages I need the bookmarklet to function in use frames (in framesets). Is it possible for a jQuery-UI dialog to work inside a frame?
Currently, when I encounter a page with a frameset, I creating my dialog like this:
var frame = window.frames[0];
var div = $(frame.document.createElement("div"));
div.html("My popup contents");
div.dialog( ... );
The result is that jQuery appends the ui-widget div to the main document, rather than the frame's document. Since the main document is just a frameset, nothing is displayed. I can't find any options in the jquery-ui API to specify which document the widgets should be constructed in. The bookmarklet will necessarily be running (or at least starting) from within the context of the outer document.
I'm aware that it won't be possible to display an overlay over the frames; I'm comfortable with display just in a single frame. Also, some other notable bookmarklets fail to function on pages with framesets, so this may be a common problem.
Suggestions?
Bookmarklets typically don't use jQuery. Most bookmarklets open a window which has jQuery.
Here's what I ended up doing: rather than attempting to display within or over a frame, I just had the bookmarklet rewrite the page to remove the framesets and add my own body and content to the page. This allows the bookmarklet to still introspect the frames and get data that it needs from them to construct the overlay prior to removing the framesets, but allows the overlay to still work.
Something like this:
if (window.frames) {
for (var i = 0; i < window.frames.length; i++) {
// ... grab data from the frame ...
}
}
if ($("frameset")) {
$("html").children("frameset").remove();
document.body = document.createElement("body");
$("html").append(document.body);
// ... add my stuff to body ...
}

Resources