How to make route regex parameters optionals with Zend ?
I try to make well formatted URLs ("search?s=mp&t=w" instead of "search/index/s/mp/t/w") for search filters, ex. :
Popularity
Most popular (s=mp)
Most viewed (s=mv, default)
Top rated (s=tr)
Most commented (s=mc)
Period
All period (t=a, default)
Today (t=d)
This week (t=w)
This month (t=m)
So, to get all top rated items from today i will have : search?s=tr&t=d
With regex routes i must specify defaults values and the problem is that the url view helper generates links with the default values and not with the current values.
Here is my route :
resources.router.routes.search.type = "Zend_Controller_Router_Route_Regex"
resources.router.routes.search.route = "search\?s\=(.+)\&t\=(.+)"
resources.router.routes.search.map.1 = s
resources.router.routes.search.map.2 = t
resources.router.routes.search.defaults.module = front
resources.router.routes.search.defaults.controller = search
resources.router.routes.search.defaults.action = index
resources.router.routes.search.defaults.s = mv
resources.router.routes.search.defaults.t = a
resources.router.routes.search.reverse = "search?s=%s&t=%s"
and links :
<div class="filters note">
<div class="filters-content">
<h3>Popularity</h3>
<ul class="filters-list">
<li>
<a href="<?=$this->url(array('s' => 'mp'), 'search')?>">
Most popular
</a>
</li>
<li>
<a href="<?=$this->url(array('s' => 'mv'), 'search')?>">
Most viewed
</a>
</li>
<li>
<a href="<?=$this->url(array('s' => 'tr'), 'search')?>">
Top rated
</a>
</li>
<li>
<a href="<?=$this->url(array('s' => 'mc'), 'search')?>">
Most commented
</a>
</li>
</ul>
</div>
</div>
<div class="filters period">
<div class="filters-content">
<h3>Period</h3>
<ul class="filters-list">
<li>
<a href="<?=$this->url(array('t' => 'a'), 'search')?>">
All period
</a>
</li>
<li>
<a href="<?=$this->url(array('t' => 'd'), 'search')?>">
Today
</a>
</li>
<li>
<a href="<?=$this->url(array('t' => 'w'), 'search')?>">
This week
</a>
</li>
<li>
<a href="<?=$this->url(array('t' => 'm'), 'search')?>">
This month
</a>
</li>
</ul>
</div>
</div>
For example, if current page is "search?s=tr&t=d" and i clic on "This week", the link is : "search?s=mv&t=w" instead of "search?s=tr&t=w" because of the default values.
I must specify default values or i get an error.
Any idea ?
Thanks,
Benjamin.
I haven't used the regex routes, but I have seen this error. Basically, the defaults.[param] parts need values. I my custom route, I am setting them to be empty:
; Navigation ID Route (uses navigation id)
resources.router.routes.nav.route = "p/:id/:title/*"
resources.router.routes.nav.defaults.module = "default"
resources.router.routes.nav.defaults.controller = "index"
resources.router.routes.nav.defaults.action = "index"
resources.router.routes.nav.defaults.id =
resources.router.routes.nav.defaults.title =
resources.router.routes.nav.reqs.id = "\d+"
resources.router.routes.nav.reqs.title = ".*"
are you sure you want to do it this way? a few times i've tried to "fight the framework" but found out the framework knew better (grin). can i suggest another way? (that Google may like better: makes your URLs more "people friendly" too). Use URLs like
/top-rated-widgets/today
/most-viewed-widgets/this-month
/most-commented-widgets
where you replace "widgets" with whatever your site is about eg "videos", "blog posts", "unicycles" whatever.
then each of the above routes to your search controller
Related
I have a Django project contains multiple templates like this:
<body>
<div>
<ul>
<li>
<a href="#">
customers
</a>
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2"></script>
<!-- place project specific Javascript in this file -->
<script src="{% static 'js/project.js' %}"></script>
<script>
{% include 'vue_dashboard.js' %}
</script>
</body>
the vue_dashboard.js file is the following
var app = new Vue({
el: '#vue_app',
delimiters: ["{(", ")}"],
data: function () {
return {
selected: 'group',
}
},
methods: {
change_selected: function (type) {
this.selected = type;
},
}
Now, I'm trying to use this vue method inside my <a> tag for example like this:
<a href="#" onclick="change_selected(type='customers')">
customers
</a>
then inside some other div obtain the value of the selected variable in the data section:
<div>
{{selected}}
</div>
I've also tried:
{% verbatim %}
{{ selected }}
{% endverbatim %}
and few other things, I know I'm using it wrong, but what's the right way to do so? I'm not even sure if my template can communicate with the vue file like this
Change
<a href="#" onclick="change_selected('customers')"> <!-- removed type= -->
customers
</a>
this should work with verbatim but you can also change delimiters in vue.js like following
delimiters: ["{(", ")}"],
which you did but didn't change {{ to {( so please change like this:
<div>
{( selected )} // <= change here
</div>
The first problem was like #ashwin said with the brackets, the other one was that the Vue app closing brackets were missing, also onclick doesn't work, I replaced it with #click, then everything was okay
<ul class="products-grid">
<li class="item">
<div class="product-block">
<div class="product-block-inner">
<img src="#/producta.jpg">
<h2 class="product-name">Product A</h2>
<div class="price-box">
<span class="regular-price" id="#">
<span class="price">Rs 1,849</span>
</span>
</div>
</div>
</div>
</li>
<li class="item">
<div class="product-block">
<div class="product-block-inner">
<img src="#/productb.jpg">
<h2 class="product-name">Product B</h2>
<div class="price-box">
<span class="regular-price" id="#">
<span class="price">Rs 1,849</span>
</span>
</div>
</div>
</div>
</li>
</ul>
I am at this moment scraping the item in a loop.
products = response.xpath('//ul[#class="products-grid"]//li//div[#class="product-block"]//div[#class="product-block-inner"]').extract()
After getting the product-block-inner node, I save it into products and then I will have to loop like
for product in products:
// parse the div.product-block-inner further deep down
// to get name, price, image etc
// and save it to a dict and yeild
pass
Is this possible that i get text, href for all div.product-block-inner in the final list without looping
Yes, but it's very confusing, for example you could try this:
products = response.xpath(
'//ul[#class="products-grid"]//li//div[#class="product-block"]//div[#class="product-block-inner"]'
).css(
'.product-name a::attr(href), .product-name a::text, .price::text'
).extract()
but I would suggest to always loop (btw, why do you call extract() when you assign it to products?)
products = response.xpath(
'//ul[#class="products-grid"]//li//div[#class="product-block"]//div[#class="product-block-inner"]'
)
for product in products:
yield {'name': product.css('.product-name a::text').extract_first()
'url': product.css('.product-name a::attr(href)').extract_first()
'price': product.css('.price::text').extract_first()}
(I've used css selectors in this case because the equivalent xpaths are longer, but the same can also be achieved using xpath)
I am trying to choose an element("Classic") from a dynamic dropdown list. Problem is that word Classic contains 2 elements.
Html page is:
<ul id="dynamic-14" class="results" role="list">
<li class="results-dept result">
<div dynamic-102" class="results" role="option">
<span class="match"/>
</div>
</li>
<li class="results-dept result">
<div dynamic-12" class="results" role="option">
<span class="match"/>
Classic
</div>
</li>
<li class="results-dept result">
<div dynamic-1022" class="results" role="option">
<span class="match"/>
Classic numbers
</div>
</li>
I tried to do it with xpath using:
//ul[#class="results"] //div[contains(.,'Classic')]
but it gives me back 2 values so robot framework can't choose one I need.
user normalize-space() function to get rid of the leading and trailing whitespace.
//ul[#class="results"] //div[ normalize-space(.)='Classic']
I do not know how to set even spacing in my navbar:
there is a bigger gap between Articles and User Profil than User Profil and Admin.
There is a bigger gap between Admin and DB than User Profil and Admin.
My code is:
<li class="blog-nav-item">
<a class="blog-nav-item" href="#" class="blog-nav-item active" data-toggle="dropdown" role="button" aria-expanded="false">Articles<span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li>All</li>
<li>Create New</li>
</ul>
</li>
<a class="blog-nav-item" href="/accounts/profil/">User Profil</a></li>
<a class="blog-nav-item" href="/admin/">Admin</a></li>
<li class="blog-nav-item">
DB<span class="caret"></span>
<ul class="dropdown-menu" role="menu">
<li>DB: JSON</li>
<li>DB: XML</li>
</ul>
</li>
That is because, by default, there is different margin spacing for a simple list element and a drop down menu.
Override the settings by:
.blog-nav-item > a{
margin-right:10px;
}
.blog-nav-item > .dropdown-menu{
margin-right:10px;
}
Adding these classes to the css will help
I'm trying to set multiple css classes on one element.
Unfortunately this doesn't work, as it returns: LanguageError: Duplicate attribute name in attributes.
<ul>
<li tal:repeat="item mainnav"
tal:attributes="class 'first' if repeat.item.start else nothing;
class 'last' if repeat.item.end else nothing;
class 'active' if item.active else nothing">
<a tal:attributes="href item.href" tal:content="item.title">title</a>
</li>
</ul>
Combining those 3 cases into one expression makes it quite complicated, because there are 6 different css states:
first + active
first
last + active
last
active
(none)
There are 2 possible solutions that I can think of:
-> check each combination inline:
<ul>
<li tal:repeat="item mainnav"
tal:attributes="
class 'first active' if (repeat.item.start and item.active) else
'first' if repeat.item.start else
'last active' if (repeat.item.end and item.active) else
'last' if repeat.item.end else
'active' if item.active else nothing">
<a tal:attributes="href item.href" tal:content="item.title">title</a>
</li>
</ul>
-> create a method that returns the combined css classes
Now, is there a better approach and if not, which of those 2 is better (probably the latter one, as if it gets more complicating the inline script will become unreadable/unmanageable).
BTW, are there any good resources and examples about Chameleon, TALES (other than http://chameleon.repoze.org/docs/latest)
You can use tal:define multiple times to define the various parts of your class string, then construct the actual attribute from those parts:
<tal:loop repeat="item mainnav">
<li tal:define="class_first 'first' if repeat.item.start else '';
class_last 'last' if repeat.item.end else '';
class_active 'active' if item.active else '';"
tal:attributes="class string:$class_first $class_last $class_active">
<a tal:attributes="href item.href" tal:content="item.title">title</a>
</li>
</tal>
This could result in an empty class attribute, which is harmless.
As for additional documentation; Chameleon is an implementation of TAL, originally developed for Zope Page Templates. As such, you'll find a lot of documentation for the latter also applies to Chameleon, as long as you take into account that Chameleon's default TALES modus is python:, while ZPT defaults to path: instead. The Advanced Page Templates chapter of the Zope Book applies to Chameleon as well, for example.
In Chameleon you can do:
<ul>
<li tal:repeat="item mainnav"
class="${'first' if repeat.item.start else ''}
${'last' if repeat.item.end else ''}
${'active' if item.active else ''">
<a tal:attributes="href item.href" tal:content="item.title">title</a>
</li>
</ul>
[Edit]
Or better like this:
<ul>
<li tal:repeat="item mainnav"
class="${('first ' if repeat.item.start else '') +
('last ' if repeat.item.end else '') +
('active' if item.active else '')}">
<a tal:attributes="href item.href" tal:content="item.title">title</a>
</li>
</ul>
You're not using tal:condition, it has a purpose. I don't like overly nested conditionals, gets you no where.
Haven't tested this but you may get the idea.
<ul>
<tal:myloop tal:repeat="item mainnav">
<li tal:condition="item.active" tal:attributes="class
'active first' if repeat.item.start
else 'active last' if repeat.item.end
else 'active'">
<a tal:attribute="href item.href" tal:content="item.title"></a>
</li>
<li tal:condition="not item.active" tal:attributes="class
'first' if repeat.item.start
else 'last' if repeat.item.end else None">
<a tal:attribute="href item.href" tal:content="item.title"></a>
</li>
</tal:myloop>
</ul>