If element hasClass, add another class to its title value - this

I'm using slick carousel, and once a div is active I want to open the corresponding description.
Problem I'm having is with this code:
if ($('div').hasClass('active')) {
var title = $(this).attr('title');
$('ul li').removeClass('open');
$(title).addClass('open');
}
What I'm trying to achieve:
Once a div gets class 'active', I want to take its title value, and use it as a id link to list element I want to display(add class to).
Here is a FIDDLE.

Use event handling, not class monitoring.
The slick carousel API has events for this, I believe you want to use the afterChange event to act on the active element after it has been made visible.
Check out the docs and examples, especially the section titled "Events" on Slick page: http://kenwheeler.github.io/slick/
And I think you don't want to use title attribute for this because that is for tooltips. I recommend data-* attributes instead. And element IDs should generally start with a letter and not a number (was required in HTML4 and makes life easier when mapping IDs to JavaScript variables; though if you are using HTML5 I think this requirement is no longer in effect).
HTML
<div id="carousel">
<div data-content-id="content1">
Selector 1 </div>
<div data-content-id="content2">
Selector 2 </div>
<div data-content-id="content3">
Selector 3 </div>
</div>
<ul class="content">
<li id="content1">Content 1</li>
<li id="content2">Content 2</li>
<li id="content3">Content 3</li>
</ul>
JavaScript
$('#carousel').on('afterChange', function(event, slick, currentSlide) {
// get the associated content id
var contentId = $(slick.$slides.get(currentSlide)).data("content-id");
if(contentId && contentId.length)
{
var $content = $("#" + contentId);
$(".content>li").removeClass("open"); // hide other content
$content.addClass("open"); // show target content, or whatever...
}
});

I have found a solution:
$('.slider').on('afterChange', function(event, slick, currentSlide, nextSlide){
var contentId= $(slick.$slides.get(currentSlide)).data('content');
if(contentId)
{
$(".content li").removeClass('open');
$('#' + contentId).addClass('open');
}
});
Working fiddle

Related

Using knockoutjs to data-bind list data into bxslider

I have managed to get the code to display list data which is driven from a sharepoint list. The list only contains one column in each row item which is called Title. I need to display the titles through the bxslider, one item per slide.
Usual bxslider html
<ul class="bxslider">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
My html implementing data-bind
<ul class="bxslider" data-bind="foreach: items">
<li data-bind="text: Title"></li>
</ul>
For some reason the 'Titles' are all being generated in one li tag, rather than creating an li tag for each item in the list.
If anyone has come across this problem before or have any advice or suggestions it would be very much appreciated.
View Model
var items = ko.observable();
jQuery(document).ready(function () {
jQuery.getJSON( "ListURLHERE", {}, dataCallBack
);
ko.applyBindings();
});
function dataCallBack(data) {
items(data.d.results);
}
I have found the answer to the problem!
I was initiating the bxslider function before the knockout js code. I had to implement the bxslider function into the dataCallBack function and it worked.
function dataCallBack(data) {
News(data.d.results);
jQuery(document).ready(function(){
jQuery('.bxslider').bxSlider();
});
}
Thanks for your help everyone!

$scope.$apply and ng-include

I'm using a $scope.$apply to trigger the view to update based on a changed variable in the scope. However, I have another line in the html that is an ng-include,
<div data-ng-include data-ng-src="'views/partials/_menubar.html'"></div>
error message
When I remove the ng-include and replace it with a static call there is no error. Here is the template that I'm including as well:
<div class="menu" ng-controller="MenuController">
<div style="display: inline-block">
Hello!
</div>
<ul class="menu_dropdown">
<li class="menu_item">Test1</li>
<li class="menu_item">Test2</li>
<li class="menu_item">Test3</li>
</ul>
</div>
The code for menu controller is
app.controller('MenuController', function($scope) {
});
ng-src is used to allow elements that usually have a src (like anchors or images) to apply the src tag only after angular's digest, not for inclusion of templates in ng-include. See ng-include docs and ng-src docs.
A safe way to specify the src using ng-include would be like this:
<div data-ng-include="src='views/partials/_menubar.html'"></div>
or
<div data-ng-include="'views/partials/_menubar.html'"></div>
If you must have the src separately, it's data-src and not data-ng-src:
<div data-ng-include data-src="'views/partials/_menubar.html'"></div>
see plnkr.
edit: To address your error message.. you'll see that message if you've bound a function to the scope which changes every time it is called.
For example, this will cause such an error:
// controller
$scope.getQuote = function(){
return 'someViewName' + Math.ceil(Math.random() * 10) + '.html';
};
// view
<div data-ng-include="{{getQuote}}"></div>
The problem with ng-include was actually a red herring. The real problem was trying to change the window.history, as seen in the thread here. My guess is because the ng-include directive references $location when it attempts to get resources.

Global search box in angular

I want to implement a search box that changes what it searches based on whichever controller is being used. If you are on the "posts" view it will search the posts api, if you are on the videos view, it searches the videos api. It seems the search box would need its own controller maybe. I'm pretty sure I need to inject a search service into all the model controllers but I'm not exactly sure how to change the url it searches or tie the input to the different controller scopes.
So any ideas how to have a global search box that changes where it searches based on whichever controller is making use of it and tying its state back into a changing view?
To make a resource call dynamic api i would first create two $resources that map to your two endpoints, posts and videos. Then put an ng-change event on your global search that calls a function in your base controller.
This function firsts need to figure out what api to search. Then make the appropriate api call. The important part is in the callback and i think this is what you are looking for.
In the callback you could $broadcast the resp data from your api query. Each of your controllers will be listening for an event with an $on function. The listeners will then populate the correct scope variable with the callback data.
Pseudo below.
Same html layout with ng-change
<html>
<body ng-controller="AppController">
<form>
<label>Search</label>
<input ng-model="global.search" ng-change="apiSearch()" type="text" class="form-control" />
</form>
<div ui-view="posts">
<div ng-controller="PostController">
<p ng-repeat="post in posts | filter: global.search">{{ post.name }}</p>
</div>
</div>
<div ui-view="videos">
<div ng-controller="VideoController">
<p ng-repeat="video in videos | filter: global.search">{{ video.name }}</p>
</div>
</div>
</body>
</html>
AppController
.controller('AppController', function ($scope, PostService, VideoService) {
$scope.apiSearch = function() {
// Determine what service to use. Could look at the current url. Could set value on scope
// every time a controller is hit to know what your current controller is. If you need
// help with this part let me know.
var service = VideoService, eventName = 'video';
if ($rootScope.currentController == 'PostController') {
service = PostService;
eventName = 'post';
}
// Make call to service, service is either PostService or VideoService, based on your logic above.
// This is pseudo, i dont know what your call needs to look like.
service.query({query: $scope.global.search}, function(resp) {
// this is the callback you need to $broadcast the data to the child controllers
$scope.$broadcast(eventName, resp);
});
}
})
Each of your child controllers that display the results.
.controller('PostController', function($scope) {
// anytime an event is broadcasted with "post" as the key, $scope.posts will be populated with the
// callback response from your search api.
$scope.$on('post', function(event, data) {
$scope.posts = data;
});
})
.controller('VideoController', function($scope) {
$scope.$on('video', function(event, data) {
$scope.videos = data;
});
})
Client side filtering.
If you are not looking for anything to crazy that can be achieved in a super simple way for global search. I didnt even know if this would work so i just did a quick test and it does. Obviously this could be solved in a much more detailed and controlled way using services and injecting them where they are needed. But since i don't know excatly what you are looking for i will provide this solution, if you like it, great accept it. If you don't i could probably help you with service injection solution
Quick solution is to have an app wide contoller with $rootScope ng-model. Lets call it global.search.
$rootScope.global = {
search: ''
};
For the app wide search input.
<form>
<label>Search</label>
<input ng-model="global.search" type="text" class="form-control" />
</form>
In separate partials you just need to filter data based on the global.search ng-model. Two examples
<p ng-repeat="post in posts | filter: global.search">{{ post.name }}</p>
Second template with different scope
<p ng-repeat="video in videos | filter: global.search">{{ video.name }}</p>
Note how they both implement | filter: global.search. Whenever global.search changes, any filters in the current view will be changed. So posts will be filtered on the posts view, and videos on the videos view. While still using the same global.search ng-model.
I tested this, it does work. If you need more detail explaining the setup and child controller hierarchy let me know. Here is a quick look at a full template
<html>
<body ng-controller="AppController">
<form>
<label>Search</label>
<input ng-model="global.search" type="text" class="form-control" />
</form>
<div ui-view="posts">
<div ng-controller="PostController">
<p ng-repeat="post in posts | filter: global.search">{{ post.name }}</p>
</div>
</div>
<div ui-view="videos">
<div ng-controller="VideoController">
<p ng-repeat="video in videos | filter: global.search">{{ video.name }}</p>
</div>
</div>
</body>
</html>

Orchard Alternate Shape Template Not Displaying Values

I'm new to Orchard and have watched both the Pluralsight "Orchard Fundamentals" and "Advanced Orchard" tutorials. Its a great platform, but I'm having a hard time wrapping my head around a couple of things.
I'd like to create a blog showcase banner on the home page only that rotates blog posts on the site. I have the HTML sliced up and functioning on an HTML template. The banner looks like this:
http://arerra.com/news-slideshow.jpg
So far I have done the following:
I've created a Blog called "Articles" and have placed a single post in there for testing.
Added a Layer called "ArticleList" where I have placed a Widget for "Recent Blog Posts"
I've created a custom layout for the home page called "Layout-Url-HomePage.cshtml" in my theme.
In my Theme "Views" folder, I have created a file called "Widget.Wrapper.cshtml" with only #Display(Model.Child) in it to remove the <article><header/><footer /><article> tags globally from the widgets.
Added a file in "Views > Parts > Blogs.RecentBlogPosts.cshtml" to control the layout of my shape. The code is the following:
#using Orchard.ContentManagement;
#{
IEnumerable<object> blogPosts = Model.ContentItems.ContentItems;
}
#if (blogPosts != null) {
<div class="container news-slider">
<ul class="slide-images">
#foreach (dynamic post in blogPosts) {
string title = post.Title;
ContentItem item = post.ContentItem;
<img src="/Themes/MountainWestHoops/Content/img/placeholder-700x380.jpg" alt="#title" class="active" />
}
</ul>
#foreach (dynamic post in blogPosts) {
string title = post.Title;
string body = post.Body;
ContentItem item = post.ContentItem;
<div class="featured-story threeD active">
<h1>#title</h1>
<p>#body #Html.ItemDisplayLink("READ MORE", item)</p>
</div>
}
<aside>
<ul class="tabs">
#foreach (dynamic post in blogPosts) {
string title = post.Title;
string url = post.Url;
ContentItem item = post.ContentItem;
<li><h3>#title</h3></li>
}
</ul>
<div class="ad-three-day-trial">
<img src="/Themes/Content/img/placeholder-260x190.gif" />
</div>
</aside>
</div>
}
My HTML is rendering properly, but none of the values that I have specified are showing up.
I am using the "Shape Tracer" module to see what template is being used. What is funny, is that the #Html.ItemDisplayLink("READ MORE", item) is rendering the article's URL, and if I replace the "READ MORE" with the string title, the title renders properly.
What am I doing wrong here that is causing strings to not display? Am I missing a larger point and misunderstanding the fundamentals? The tutorials seems to say that you can simply move around parts, but in this case, I need to have very specific markup for this slider to work.
Seems like your source was http://weblogs.asp.net/bleroy/archive/2011/03/27/taking-over-list-rendering-in-orchard.aspx
That is a rather old post, and the way the title is handled has changed since then.
The DisplayLink works because the only correct property here is post.ContentItem, which is what that API takes. post.Title and post.Body on the other hand are very likely null, which is why you see nothing. To access the title, you can use post.ContentItem.TitlePart.Title and to get the body, post.ContentItem.BodyPart.Text.

Orchard CMS: Logon Page doesn't work with my custom layout

I am very new to Orchard.
I have created a new theme, based on the Minty theme. The only real change is the layout, where I have adapted the html from an existing asp.net masterpage to match the orchard style razor layout.cshtml. I have experience with MVC and razor, so no problem on that side... unless I have missed something vital.
The problem is the login page. Clicking the sign in link takes me to the correct url without errors, but not login form gets rendered. I have checked that this is the case by Inspecting Element in google chrome.
I am aware that setting up widgets, etc, I can make content appear. However, I can't find how the login form gets inserted when the login url gets requested. I presume it uses the Orchard.Users module, but not sure how. Does it need a specific zone? I can't see why, but see how else.
As a result, I can't solve my problem...
Any pointers?
Any books or other learning media?
The code for my layout.cshtml is:
#functions {
// To support the layout classifaction below. Implementing as a razor function because we can, could otherwise be a Func<string[], string, string> in the code block following.
string CalcuClassify(string[] zoneNames, string classNamePrefix) {
var zoneCounter = 0;
var zoneNumsFilled = string.Join("", zoneNames.Select(zoneName => { ++zoneCounter; return Model[zoneName] != null ? zoneCounter.ToString() : "";}).ToArray());
return HasText(zoneNumsFilled) ? classNamePrefix + zoneNumsFilled : "";
}
}
#{
/* Global includes for the theme
***************************************************************/
SetMeta("X-UA-Compatible", "IE=edge,chrome=1");
Style.Include("http://fonts.googleapis.com/css?family=Handlee");
Style.Include("http://html5shiv.googlecode.com/svn/trunk/html5.js");
Style.Include("site.css");
Script.Require("jQuery").AtHead();
Script.Require("jQueryUI_Core").AtHead();
Script.Require("jQueryUI_Tabs").AtHead();
Script.Include("http://cdnjs.cloudflare.com/ajax/libs/modernizr/2.0.4/modernizr.min.js").AtHead();
Style.Include("TagDefaults.css");
Style.Include("LayoutStructure.css");
Style.Include("LayoutStyling.css");
Style.Include("TopMenu.css");
Style.Include("LeftBlock.css");
Style.Include("RightBlock.css");
Style.Include("MenuAdapter.css");
Style.Include("Content.css");
Style.Include("FloatedBoxes.css");
Style.Include("Helen.css");
/* Some useful shortcuts or settings
***************************************************************/
Func<dynamic, dynamic> Zone = x => Display(x); // Zone as an alias for Display to help make it obvious when we're displaying zones
/* Layout classification based on filled zones
***************************************************************/
//Add classes to the wrapper div to toggle aside widget zones on and off
var asideClass = CalcuClassify(new [] {"Sidebar"}, "aside-"); // for aside-1, aside-2 or aside-12 if any of the aside zones are filled
if (HasText(asideClass)) {
Model.Classes.Add(asideClass);
}
//Add classes to the wrapper div to toggle tripel widget zones on and off
var tripelClass = CalcuClassify(new [] {"TripelFirst", "TripelSecond", "TripelThird"}, "tripel-"); // for tripel-1, triple-2, etc. if any of the tripel zones are filled
if (HasText(tripelClass)) {
Model.Classes.Add(tripelClass);
}
//Add classes to the wrapper div to toggle quad widget zones on and off
var footerQuadClass = CalcuClassify(new [] {"FooterQuadFirst", "FooterQuadSecond", "FooterQuadThird", "FooterQuadFourth"}, "split-"); // for quad-1, quad-2, etc. if any of the quad zones are filled
if (HasText(footerQuadClass)) {
Model.Classes.Add(footerQuadClass);
}
var slideshowClass = CalcuClassify(new[] {"HomeSlideshow"}, "slideshow-");
if (HasText(slideshowClass)) {
Model.Classes.Add(slideshowClass);
}
/* Inserting some ad hoc shapes
***************************************************************/
//WorkContext.Layout.Header.Add(New.Branding(), "5"); // Site name and link to the home page
//WorkContext.Layout.Footer.Add(New.BadgeOfHonor(), "5"); // Powered by Orchard
WorkContext.Layout.Footer.Add(New.User(), "10"); // Login and dashboard links
/* Last bit of code to prep the layout wrapper
***************************************************************/
Model.Id = "layout-wrapper";
var tag = Tag(Model, "div"); // using Tag so the layout div gets the classes, id and other attributes added to the Model
}
#tag.StartElement
<a name="top"></a>
<div id="SiteHeader">
</div>
<div id="PageContainer">
<div style="position: absolute; Left:-80px; top:-88px;z-index:1000;">
<img id="bird" title="Pheasant" src="/Themes/TheFarmsBlogs/Styles/Images/PositionedImages/pheasant.gif" />
</div>
<div class="SiteMenu"><p>Hello Menu</p></div>
<div id="Specialized">
<div id="PageName">
<!--
PageName NOT in use!
-->
</div>
#if (Model.RightColumn != null) {
<div id="RightCol">
#Zone(Model.RightColumn)
</div>
}
<!-- Page divided into two main columns, of which the left column is subdivided as necessary -->
<div id="LeftCol">
<div id="PageBanner">
<div id="PageBannerLeft">
#if (Model.MainImage != null) {
<div id="PageBannerImage">
#Zone(Model.MainImage)
</div>
}
#if(Model.TheStrip != null) {
<div id="TheStrip">
#Zone(Model.TheStrip)
</div>
}
</div>
</div>
<div id="SpecializedContent">
#if(#Model.content != null)
{
#Zone(Model.content)
}
</div>
</div>
<div id="SpecializedFooter">
</div>
</div>
<div id="PageFooter">
#if (Model.FooterPage != null){
#Zone(Model.FooterPage)
}
</div>
</div>
<div id="SiteFooter">
#Display(Model.Footer)
The Farms Ltd - © 2007
</div>
#tag.EndElement
PS: the branding and badge of honour are commented out as I am only enabling bit by bit to eliminate the source of errors. It will be in the live site.
ADDENDUM:
See Bertrand Le Roy's answer below. The Orchard.Users module requires a Content zone with a Capital C. That instantly cured the problem.
I added this as Bertrand's response was tentative, and I wanted to reinforce that the problem was the name of the zone.
In Orchard.Users, look for Controllers/AccountController.cs. In there, there is a LogOn action. It creates a LogOn shape that it then puts in a shape result. This then gets resolved as the Views/LogOn.cshtml template (which you can override in your theme by just dropping a file with the same name in there, for example a copy of the original that you can tweak). The LogOn template will be rendered within the theme's layout, in the Content zone. Does this answer your question?
I think the mistake you made was to name your Content zone content (notice the casing).

Resources