Angular Dragula and ng-repeat $index - javascript

I have an ng-repeat of rows. I set them up so you can reorder them with drag-and-drop using Angular Dragula. This works fine, but the ng-repeat $index remains the initial value for each item after dragging. See screen captures below before and after a drag of the "Recipient" row.
Is there a way to update the index? I also want to re-order the menu with javascript, with that button in each row, but for that to work I need to get the current $index (and decrement/increment it) and that won't work if the index is incorrect like this.
Before Drag
After Drag

For what it's worth, this is the answer:
<div class="table-cards-body" dragula="'first-bag'" dragula-model="home.quickReceiveFields">
<div class="table-cards-row drag-item" ng-repeat="field in home.quickReceiveFields track by $index">
<div class="one">{{field.name}}</div>
<div class="two">{{field.type}}</div>
<div class="three">{{field.default}} {{$index}}</div>
<div class="four"><input type="checkbox" ng-model="field.display"></div>
<div class="five"><input type="checkbox" ng-model="field.retain"></div>
<button class="btn btn-default btn-xs" ng-click="home.moveItemUp($index)">^</button>
</div>
</div>
Despite what the docs say on the angular-dragula site, you need to put the dragula-model attribute on the container into which you put the repeating items. Not the ng-repeat itself.

Try to add in ng-repeat track by $index.

Related

Angular 2+ switching betweens elements, based on click

So, I'm trying to use a angular way to send the buttons created to either the #start or #finish divs, based on click on the buttons selected, so in a way they would send themselves if you may if they follow a condition, which in this case is to be either inside of the #start or #finish divs. With Jquery, I just check what's the parent of certain element, if matches one, I send it to the other, and vice versa. Now with angular, I have been looking into the whole, rendering and stuff, but my head can't just understand the overall picture, and I even though I was able to click and send the element clicked to a different div, I couldn't do it with the other buttons created, and also with the button that was first clicked, in the other div.
<div class="ui raised very padded text container segment"
#finish>
</div>
<div class="ui raised very padded text container segment" #start>
<button
*ngFor='let word of ge_array'
(click)="goToNext()"
>{{word}}</button>
</div>
Does anybody know how to tackle this situation?
Renan, I would change all your plan. I have an array of element. this elements have a property "place". I will show in two div one for "plan1" and the other for "plan2".
//the .ts is like
items:any[]=[{word:'uno',place:1},{word:'dos',place:1},{word:'tres',place:2}]
get items1(){
return this.items.filter(it=>it.place==1);
}
get items2(){
return this.items.filter(it=>it.place==2);
}
//the .hmtl like
<h2>place 1</h2>
<button *ngFor="let item of items1" (click)="item.place=2">{{item.word}}</button>
<h2>place 2</h2>
<button *ngFor="let item of items2" (click)="item.place=1">{{item.word}}</button>
I would prefer using a getter and have "item1" and "item2". the click make that the property "place" of the item becomes 2 in place 1 and becomes 1 in place2
You can make two *ngFor over the same array also using a *ngIf like
<h1>place 1</h1>
<div *ngFor="let item of items">
<button *ngIf="item.place==1" (click)="item.place=2">{{item.word}}</button>
</div>
<h1>place 2</h1>
<div *ngFor="let item of items">
<button *ngIf="item.place==2" (click)="item.place=1">{{item.word}}</button>
</div>
and forget the getters

How to make the element `display: none` by clicking the button

I want to implement a function to hide elements when button is clicked in an application built using Angular 2.
<div *ngFor="let todo of todos" class="col-sm-8 col-sm-offset-2">
<div class="panel panel-default step"> // from here
<div class="panel-body">
<a [routerLink]="['/todo', todo.id]">{{todo.title}}</a>
<button (click)="delete(todo.id)" type="button" class="btn btn-success btn-circle pull-right"><i class="glyphicon glyphicon-ok"></i></button>
</div>
</div>//so far
</div>
I want to know how to make the todo disappear when the button is clicked with the above code.
Although it is an elementary question, please teach.
You should be removing that todo item as below
delete(id:number){
this.todos.splice(id,1)
}
Small changes to Aravinds answer, since we need to find the index of the todo, so that right todo will be deleted
I like to pass the object in delete:
(click)="delete(todo)"
Need to find the index of the todo and then splice.
delete(todo) {
this.todos.splice(this.todos.indexOf(todo),1);
}
Update:
Just realized that it seems that you only want to hide the todo, and not delete it entirely. Well this can be achieved with the following. Upon initialization all todo's are visible. We could introduce a new variable to todo, when we want to hide it. So initially this property doesn't exist, so we mark inside the iteration: *ngIf="!todo.hidden", complete code:
<div *ngFor="let todo of todos">
<div class="panel-body" *ngIf="!todo.hidden">{{todo.name}}
<button (click)="hide(todo)" type="button">Hide Todo</button>
</div>
</div>
And the hide function sets the hidden property as true:
hide(todo) {
todo.hidden = true;
}
This doesn't obviously give the option to actually unhide the todo, but that was not mentioned in your question.
I think this is not the right way to 'remove' an element on a todo list. #Aravind answer is better for it.
But if you really want to use display:none, you can do this like :
https://plnkr.co/edit/eXwAWWeH9N6REsqpzFCq?p=preview
<element ng-style="displayStyle"></element>
<button value="displayNone" ng-click="displayStyle={'display':'none'}">Display none</button>

Ng-switch in ng-repeat issue

I have an odd issue that is driving me crazy
I basically have a list of article of different type: news, tweets and video
I am rendering them like this:
<div ng-repeat="item in items | orderBy:-timestamp track by item.id" ng-switch="item.type">
<?php
include("templates/ang-youtube.html");
include("templates/ang-tweet.html");
include("templates/ang-news.html");
?>
</div>
Inside each include i am doing something like this:
<div class="item" masonry-brick ng-switch-when="news">
...content in here
</div>
or
<div class="item" masonry-brick ng-switch-when="tweet">
...content in here
</div>
or
<div class="item" masonry-brick ng-switch-when="youtube">
...content in here
</div>
Now the problem is the youtube items are always rendered first. The order of the items (timestamp) is ignored. The tweets and news items though render correctly.
If i remove the switch and just list out the items as text then everything is in the correct order. As soon as I add the switch the youtube items are rendered first again.
Any ideas what I am doing wrong?
I have tried moving the switch inside the ng-repeater like so:
<div ng-repeat="item in items | orderBy:-timestamp track by item.id">
<div ng-switch="item.type">
<?php
include("templates/ang-youtube.html");
include("templates/ang-tweet.html");
include("templates/ang-news.html");
?>
</div>
</div>
But it makes no difference. The youtube items are always rendered first.
If i inspect the $scope.items array they are ordered correctly by timestamp.
I have also tried using an ng-if instead of a switch but get the same outcome
Any ideas? :-(
Also tried using ng-include like this:
<div ng-include="'templates/ang-{{item.type}}.html'"></div>
But that doesn't work
DOH!!! Needed to add the preserve order attribute as outlined here
https://github.com/passy/angular-masonry

Individual toggles inside an ng-repeat

I have some content coming in which I am running through a repeat, each one of these content items has to have a toggle open/close functionality to it. I figured my best bet in marking them individually is using something like the $index of the item in the repeat. I am just using a (bootstrap) class to show/hide like so :
<a ng-click="{{$index}}inner = !{{$index}}inner" >{{item.node.id}}</a>
<div class="nested " ng-class="{'collapse' : !{{$index}}inner">
So - a click toggle on the a above. This does not work because of the syntax, but what I'm wondering here is if there is a nice elegant solution to this problem. This way I tried first seems a little messy. Thanks!
The ng-repeat directive created a scope for each of the elements. Therefore you could just use the ng-click to toggle a property on that scope.
Also the syntax of your ng-class is not totally correct.
For working example see http://jsbin.com/yuyucojeco/edit?html,js,output
I like adding properties to the actual objects themselves. Then, within your ng-repeat, you can just reference the individual object (instead of trying to re-index into the array) and apply all logic as you need.
Maybe something like this:
<div ng-repeat="item in array">
<a ng-click="item.collapsed = !item.collapsed" >{{item.node.id}}</a>
<div class="nested " ng-class="{'collapse' : item.collapsed"></div>
</div>
I have implemented smt similar inside an ng-repeat using bootstrap as well like so:
here is the button:
<a data-toggle="collapse" data-target="#{{item.id}}" class="accordion-toggle btn btn-xs btn-success"></a>
then the collapsible tab:
<div class="accordion-body collapse" id="{{item.id}}"> <!-- accordion starts -->
<!-- some content here -->
</div> <!-- accordion ends -->

Using ng-model (or other solution) without breaking MVC paradigm

I recently asked a question on Stack where I was trying to obtain a DOM element's ID via AngularJS' ng-click. Essentially the answer which was given (with an important caveat was):
Use event.currentTarget vs event.target to get the element to which the binding was registered, BUT this is an atypical way to do it because it ties the controller to the DOM when ideally the controller should know nothing about the DOM.
I'm starting to get a better idea of this now, but would like some further help / clarification.
Using ng-repeat I dynamically render a number of tiles being pulled from a database and present them to the user for selection. When a user clicks on a given tile I want to be able to 'know' that element's ID (or some unique identifier key) so that I can pass it into my javascript / java and then retrieve the details for said key where they are rendered in a different, more detailed view.
I've started to research ng-model which supports the two-way MVC idea, but I'm stuck. You can see below that I'm dynamically rendering each tile with a different ng-model value which equals the tile's database key. Is this the solution I want? If so, how can I reference the ng-model value in javascript? Or if I do that, am I breaking the MVC again? If that's the case, what would be a solution which preserves the model?
Cheers
HTML:
<div ng-repeat="tile in tileResult">
<div ng-model={{tile.id}} ng-click="handleThisElement($event); changeView('panel3')" class="container-fluid btn-default tile">
<div class="row">
<div class="col-xs-9">
<div class="row">
...
</div>
</div>
<div class="col-xs-3 tile-stats">
<div class="row text-center">
...
</div>
</div>
</div>
</div>
</div>
Tie the ng-model to an object, best if it's something in the repeater. Also, ng-model is generally used with an input... not on a div, so I'm not sure what you're trying to achieve here.
You may want to initialize the value to the index (or some other default) if the value doesn't exist, this will avoid null pointers when you want to change the value later.
<div ng-repeat="tile in tileResult">
<div ng-model="tile.someDataValue" ng-init="tile.someDataValue = $index" ng-click="handleThisElement($event); changeView('panel3')" class="container-fluid btn-default tile">
...
</div>
</div>
To later reference the value, you can just access your tileResult object at the appropriate value/index
Ex:
console.log($scope.tileResult[0].someDataValue);
Or you can access the entire 'tile' on click by passing the 'tile' into a function. Ex:
<div ng-model="tile.someDataValue" ng-init="tile.someDataValue = $index" ng-click="someFunction(tile); handleThisElement($event); changeView('panel3')" class="container-fluid btn-default tile">
$scope.someFunction = function(someTile){
console.log(someTile.id, someTile); // log the id, then the entire object
}
If I understand you correctly, you want to be able to access the unique identifier for each tile.
This can easily be done without Ng-Model! One easy fix would be to set the id of each element with the unique identifier:
<div ng-repeat="tile in tileResult">
<div id="{{tile.id}}" ng-click="handleThisElement($event); changeView('panel3')" class="container-fluid btn-default tile">
<div class="row">
This way, when you pass $event to your handleThisElement function, you are able to access the id in the same way you have before.

Categories

Resources