I am using AngularJS with Angular Strap plugin for one of my projects. On the page I have a table. One of the columns contains some links.
After clicking on the link I pop-ups Angular Strap's dropdown (as contextual menu).
This menu contains two items:
details,
board.
Clicking on details item open-ups Angular Strap's aside (with some data to be shown) - this one is working well.
The problem is, the second item should redirects to a specific page (using dropdown's href attribute). My question is - how to use angular expression in this href attribute?
Part of my view's code - with dropdown:
<button type="button" class="btn btn-link"
ng-click="selectGroup(group)"
data-html="1"
data-animation="am-flip-x"
data-placement="bottom",
bs-dropdown="dropdown">
{{ group.name }}
</button>
Bs-dropdown's dropdown definitions from controller:
$scope.dropdown = [
{
text: "Details",
click: "showDetails()" // Opens up Angular Strap's aside.
},
{
text: "Board",
href: "/group/{{ $scope.group.number }}" // Not evaluated.
}
];
Clicking on "Board" dropdown's item is redirecting to something like: "127.0.0.1:8000/#/group/{{group.number}}" rather than (i.e.) "127.0.0.1:8000/#/group/2".
As a workaround I have created another method (i.e.) openBoard() as follows:
var openBoard = function() {
var groupNumber = $scope.group.number;
window.location.href="127.0.0.1:8000/#/" + groupNumber;
}
Calling this method in "Board" item's click is working, but causes some other problems and more - is not the way I want to go (semantically incorrect).
One more thing at the end. Whenever page loads (and controller has been initialized) value stored in $scope.group is undefined. I am setting a proper value whenever group link (from above mentioned table) is being clicked (and a moment before dropdown is displayed).
Anyone has any ideas how to use Angular expressions with Angular Strap's dropdown (in href)?
As $scope.group member doesn't available at starting while creating dropdown array, that will result href: "/group/{{ $scope.group.number }}" this to href: "/group/undefined". So I'd suggest you to initially load dropdown with one value,
$scope.dropdown = [
{
text: "Details",
click: "showDetails()" // Opens up Angular Strap's aside.
}
];
& whenever group values gets fetched, do assign the second dropdown option
$scope.dropdown.push({
text: "Board",
href: "/group/" + $scope.group.number
});
Hope this work around would help you, Thanks.
I think that you must change {{ $scope.group.number }} for this /group/group.number
Related
I am using AngularJS to create a page which contains a list of products that shows information such as a name, price, region etc. This is displayed kind of like an accordion with the name in the header and extra information in the body.
Since there could be a large amount of these items displayed I am using dirPagination (https://github.com/michaelbromley/angularUtils/tree/master/src/directives/pagination) to paginate these items. My markup at the moment looks like this:
<div class="custom-list" dir-paginate="asset in assets | itemsPerPage: 10 track by $index">
<div class="custom-list-item" ng-class="{'tag-hover': isHoveredTag(asset)}">
<div class="custom-list-item-heading" ng-click="toggleShowAssetDetails(asset)">
<p class="col-lg-5">{{ asset.name }}</p>
<div class="col-lg-offset-2 col-lg-3 rating">
<rating value="asset.rating" />
</div>
<button ng-click="addAsset(asset)"><span class="glyphicon glyphicon-plus"></span></button>
<div class="clearfix"></div>
</div>
<div class="custom-list-item-content" style="display: none" animate="shouldShowAssetDetails(asset)">
...
</div>
</div>
</div>
As you can see I'm using paginate in a pretty standard way just looping through the items in an array and displaying 10 per page. I also have a directive called rating which looks at a value called rating in the item. This is a number from 1 - 5 which is used to display a star rating system next to the name. The directive looks like this:
var rating = function ($compile) {
return {
restrict: "E",
scope: {
value: "="
},
link: function (scope, element, attrs) {
scope.$watch(attrs.rating, function () {
for (var i = 1; i <= 5; i++) {
if (i <= scope.value) {
var starElement = angular.element("<span class=\"icon icon-crown\"></span>");
$compile(starElement)(scope);
element.append(starElement);
} else {
var emptyStarElement = angular.element("<span class=\"icon-empty icon-crown\"></span>");
$compile(emptyStarElement)(scope);
element.append(emptyStarElement);
}
}
})
}
}
}
This looks at the value and inserts the icons based on the value of rating (e.g if the rating was 2 the directive would insert two icon-crown icon spans and 3 icon-empty icon-crown icon spans.
This was working perfectly fine before I included the pagination. However now it will only work for the first 10 items. When you change the page, the rating will not change and just keep the same icons from the previous page, even if the values are different.
I understand this is because the directive sets everything at the beginning and it will not run when you change page because the items aren't reloading they are just being shown and hidden again. But the directive is manipulating the DOM so it doesn't update when the page changes.
The problem is I don't know how to resolve this. I thought about changing the directive to look for the pagination current page instead but then I don't have access to the current list item.
I'd appreciate any help on getting the directive to update the icons when the page is changed.
Update
Here's a link to a Plunker project showing the problem I'm having: http://plnkr.co/edit/VSQ20eWCwVpaCoS7SeQq?p=preview
This is a very stripped down version of the section on my app that I'm having an issue with. There's no styling included although I have kept the CSS class structure. I've also changed the icons to use bootstrap ones just to simplify the Plunker project.
The functionality is the same however. If you go from page 1 to page 2 notice how the stars remain the same despite that fact that the asset rating values are different. However if you go to page 3 and back to page 2 or 1 they will change. The reason this happens is because there are less items on page 3 and therefore when you go back to page 1 or 2 the remaining items will be called again to retrieve the rating values.
You simply need to remove or replace track by $index.
Tracking by $index will give you the following behavior:
There is an array of max 10 length that represents the items to show. The first item will have index 0.
You go to the next page and the items in the array are replaced.
The first item in the array will still have index 0. Since you are tracking by index and the index has not changed, AngularJS will not recreate the DOM node representing the item. Since the DOM node isn't recreated, the directive will not execute this time and the rating will not update.
If you go from page 3 to page 2, the directive will execute for the 7 last elements on page 2, since they didn't exist on page 3, so they are considered new this time.
If you really need to use track by you should use a property that is actually related to the object (and unique), for example:
dir-paginate="asset in assets | itemsPerPage: 10 track by asset.name"
Demo: http://plnkr.co/edit/A80tSEliUkG5idBmGe3B?p=preview
How to refresh the variable assigned in data-ng-init whenever source data changes.
I know how to do that from controller. But is there anyway to do it from html using some directive.
Below is sample demo i have done, were you will see Car and Bike Section
When you click "Add Car" listing and counts get changed as i'm using from the direct source
When you click "Add Bike",
bikeData is not get refreshed, as it holds initial json value only.
data-ng-init="bikeData = (sorted | filter: { type: 'bike' }:true)"
Demo
That's because the bikeData isn't refreshed after. You either need to watch for changes on the your sorted object and update bikeData. Or you can simply slap the same expression you have in your data-ng-init to your ng-click directive. See demo:
http://jsfiddle.net/tomalex0/9jxQK/1/
Another option would be to get rid of the data-ng-init and bind your ng-repeat to the sorted object like you do with cars. For example:
<ul ng-repeat="item in sorted | filter: { type: 'bike' }:true">
I need to change the menu text depending on user interaction on my web page. I am using kendo web menu. So far I tried it with:
var menu1 = $("#menu1").data("kendoMenu");
menu1.element[0].childNodes[0].innerHtml = "NEW TEXT";
It works nicely (with proper indexing), but after a few changes the menu is stired up (formatting, behaviour, etc.), which makes me think this is not the "official" way to do it. Any ideas?
Instead of playing with the HTML DOM you should use some of the methods provided by KendoUI menu for managing new options.
You should take a look into:
insertBefore: Inserts an item into a Menu before the specified referenceItem.
insertAfter: Inserts an item into a Menu after the specified referenceItem.
append: Appends an item to a Menu in the specified referenceItem's sub menu.
You should use one or the other depending on where to insert.
In the following example you have two function for inserting as first option in a menu:
menu.insertBefore(
[ { text: "NEW TEXT" } ],
"#where > li:first-child"
);
Where #where is the id of the li that contains the list of options (menu items).
If you want to insert as last option in a menu:
menu.insertAfter(
[ { text: "NEW TEXT" } ],
"#where > li:last-child"
);
You can see it in action here
I finally got bindings for the options and value to work on my dropdown component. What I have now works except that all javascript after the applyBinding for the value is not executed. This is not a problem when I only have one dropdown on the page at a time.
However, most of the time, I need to use more than one. When I step through the script on such a page, it executes the applyBinding for the first dropdown and stops. No further script runs, and only the first dropdown works.
Why is this? More importantly, how do I fix it?
Here are the relevant lines of code:
$(function () {
var $thisdd = $("##ddname"); //the JQuery selector for my dropdown
var dropdownItems = getDropdownItemsFromDl("#ddname");
var newitem = ko.observable({ cname: ddcname, cvalue: ko.observable($thisdd.val()), cpublishtopic: "" });
classificationsViewModel.push(newitem());
var viewModel =
{
dditems : dropdownItems
};
$("##ddname").attr("data-bind", "value: classificationsViewModel()[" + classificationsViewModel.indexOf(newitem()) + "].cvalue, options: dditems, optionsText: 'value', optionsValue: 'key'");
ko.applyBindings(viewModel);
ko.applyBindings(classificationsViewModel()[classificationsViewModel.indexOf((newitem())].cvalue);
});
When you call ko.applyBindings, Knockout will apply the model to the entire document.
This is great if you have one model for the entire page.
It seems you want one model per dropdown. See the optional parameter on http://knockoutjs.com/documentation/observables.html#activating_knockout
You can then bind to each dropdown a separate model via something like ko.applyBindings(viewModel, $('##ddname')[0]);
I don't understand what this blurb is trying to do:
value: classificationsViewModel()[" + classificationsViewModel.indexOf(newitem()) + "].cvalue
It seems you're trying to use two different models to control the dropdown. I am fairly certain that won't end well. Try using one model to rule them all to handle all the dropdowns. I think you'll find it much easier to maintain and logic about.
I'm using Scripaculous' in place collection editor to present the user a with list of customers to assign to a contact dynamically. I am passing a value parameter as detailed in the documentation so when the select is generated it will auto-select the current associated customer (if there is not one already the default is provided.)
This works well except when the user clicks on the 'edit me' field a second time the original value is selected, not the customer that was selected most recently. This is because the original call to InPlaceCollectionEditor is unchanged. My question is whether there is any way to update the autoselect value without dynamically updating the entire scriptaculous call (this would mean selecting all the customers again via ajax, which is possible but not ideal.)
Here is the code:
<div id="editme">Test 1</div>
<script type="text/javascript">
new Ajax.InPlaceCollectionEditor(
'editme',
'/usercustomer/editCustomer/id/811',
{ collection: [['192981', "Test 1"],['192893', "Test 2"],['192894', "Test 3"] ... ],
ajaxOptions: {method: 'get'}
, value: 192893 } // specifying the value parameter auto selects object id 192893
);
</script>
Is this a limitation of InPlaceCollectionEditor or is there some other way around this?
Pass a variable instead of the hardcoded number as 'value'. Set that variable to the initial or selected value right before making the call.