Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/wcf/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Angular MatSort打断MatTable细节行动画_Angular_Animation_Angular Animations - Fatal编程技术网

Angular MatSort打断MatTable细节行动画

Angular MatSort打断MatTable细节行动画,angular,animation,angular-animations,Angular,Animation,Angular Animations,在我到这里之前,我已经为这个问题绞尽脑汁很久了。基本上,我有一个角度材质表,它使用动画创建细节行。当表格排序时,它会重新排列数据。在该过程中,某些详图行会转换为void。之后,细节行停止播放动画,即使动画事件正在触发。我怀疑MatSort以某种方式破坏了动画,但我不确定如何破坏 角材料表: <mat-table matSort [dataSource]="tableData" multiTemplateDataRows> <!-

在我到这里之前,我已经为这个问题绞尽脑汁很久了。基本上,我有一个角度材质表,它使用动画创建细节行。当表格排序时,它会重新排列数据。在该过程中,某些详图行会转换为void。之后,细节行停止播放动画,即使动画事件正在触发。我怀疑MatSort以某种方式破坏了动画,但我不确定如何破坏

角材料表:

<mat-table matSort
        [dataSource]="tableData"
        multiTemplateDataRows>

        <!-- More Column -->
        <ng-container matColumnDef="more">
            <mat-header-cell *matHeaderCellDef 
                translate>
                More
            </mat-header-cell>
            <mat-cell *matCellDef="let scheduleCourse">
                <p class="fa fa-angle-right" *ngIf="!tableData.checkExpanded(scheduleCourse)"></p>
                <p class="fa fa-angle-down" *ngIf="tableData.checkExpanded(scheduleCourse)"></p>
            </mat-cell>
        </ng-container>

        <!-- Meets Column -->
        <ng-container matColumnDef="meets">
            <mat-header-cell *matHeaderCellDef
                mat-sort-header="Meets" 
                translate>
                Meets
                <filter [data]="tableData" columnName="Meets" dataType="string"></filter>
            </mat-header-cell>
            <mat-cell *matCellDef="let scheduleCourse">
                {{scheduleCourse.Meets}}
            </mat-cell>
        </ng-container>

        <!-- Term Column -->
        <ng-container matColumnDef="term">
            <mat-header-cell *matHeaderCellDef 
                mat-sort-header="Term"
                translate>
                Term
                <filter [data]="tableData" columnName="Term" dataType="string"></filter>
            </mat-header-cell>
            <mat-cell *matCellDef="let scheduleCourse">
                {{scheduleCourse.Term}}
            </mat-cell>
        </ng-container>

        <!-- Course Name Column -->
        <ng-container matColumnDef="course">
            <mat-header-cell *matHeaderCellDef 
                mat-sort-header="Course"
                translate>
                Course Name
                <filter [data]="tableData" columnName="Course" dataType="string"></filter>
            </mat-header-cell>
            <mat-cell *matCellDef="let scheduleCourse">
                {{scheduleCourse.Course}}
            </mat-cell>
        </ng-container>

        <!-- Teacher Column -->
        <ng-container matColumnDef="teacher">
            <mat-header-cell *matHeaderCellDef 
                mat-sort-header="Teacher"
                translate>
                Teacher
                <filter [data]="tableData" columnName="Teacher" dataType="string"></filter>
            </mat-header-cell>
            <mat-cell *matCellDef="let scheduleCourse">
                {{scheduleCourse.Teacher}}
            </mat-cell>
        </ng-container>

        <!-- Room Column -->
        <ng-container matColumnDef="room">
            <mat-header-cell *matHeaderCellDef 
                mat-sort-header="Room"
                translate>
                Room
                <filter [data]="tableData" columnName="Room" dataType="string"></filter>
            </mat-header-cell>
            <mat-cell *matCellDef="let scheduleCourse">
                {{scheduleCourse.Room}}
            </mat-cell>
        </ng-container>

        <!-- Entry Date Column -->
        <ng-container matColumnDef="entry date">
            <mat-header-cell *matHeaderCellDef 
                mat-sort-header="EntryDate"
                translate>
                Entry Date
                <filter [data]="tableData" columnName="EntryDate" dataType="date"></filter>
            </mat-header-cell>
            <mat-cell *matCellDef="let scheduleCourse">
                {{scheduleCourse.EntryDate.toString() != junkDate.toString() ? scheduleCourse.EntryDate.toLocaleDateString() : ''}}
            </mat-cell>
        </ng-container>

        <!-- Dropped Date Column -->
        <ng-container matColumnDef="dropped date">
            <mat-header-cell *matHeaderCellDef 
                mat-sort-header="DroppedDate"
                translate>
                Dropped Date
                <filter [data]="tableData" columnName="DroppedDate" dataType="date"></filter>
            </mat-header-cell>
            <mat-cell *matCellDef="let scheduleCourse">
                {{scheduleCourse.DroppedDate.toString() != junkDate.toString() ? scheduleCourse.DroppedDate.toLocaleDateString() : ''}}
            </mat-cell>
        </ng-container>

        <!-- Team Column -->
        <ng-container matColumnDef="team">
            <mat-header-cell *matHeaderCellDef 
                mat-sort-header="TeamCode"
                translate>
                Team
                <filter [data]="tableData" columnName="TeamCode" dataType="string"></filter>
            </mat-header-cell>
            <mat-cell *matCellDef="let scheduleCourse">
                {{scheduleCourse.TeamCode}}
            </mat-cell>
        </ng-container>

        <!-- Expand Row 1 -->
        <ng-container matColumnDef="expandedRow">
            <td mat-cell
                *matCellDef="let scheduleCourse"
                [attr.colspan]="columns.length"
                style="width: 100%">

                <!-- Links and Actions -->
                <div class="detailRow">
                    <div class="detailItem">
                        <label style="color: #595959" translate>Course-Section</label>
                        &nbsp;
                        {{scheduleCourse.SubjectCode}}-{{scheduleCourse.Section}}
                    </div>
                    <a class="detailItem"
                        (click)="assignmentClick(scheduleCourse)"
                        translate>
                        Assignments
                    </a>
                    <a class="detailItem"
                        (click)="attendanceClick(scheduleCourse)"
                        translate>
                        Attendance
                    </a>
                    <a class="detailItem"
                        (click)="emailTeacherClick(scheduleCourse)"
                        translate>
                        Email Teacher
                    </a>
                    <a class="detailItem"   
                        (click)="gradesClick(scheduleCourse)"
                        translate>
                        Grades
                    </a>

                    <!-- Menu Button -->
                    <button class="detailItem"
                        *ngIf="showProfiles"
                        style="cursor: pointer; border: none; background-color: inherit;" 
                        [matMenuTriggerFor]="actionMenu"
                        [matMenuTriggerData]="{'scheduleCourse': scheduleCourse}">
                        <img src="./assets/images/actions.png"
                            alt="actions">
                    </button>
                </div>

                <!-- School Indicator -->
                <div *ngIf="showSchool(scheduleCourse)" 
                    class="detailRow">
                    <div class="detailItem">
                        <label style="color: #595959" translate>
                            School
                        </label>
                        &nbsp;
                        {{scheduleCourse.SchoolName}}
                    </div>
                </div>
            </td>
        </ng-container>

        <!-- Row definitions -->
        <mat-header-row *matHeaderRowDef="columns"></mat-header-row>
        <mat-row *matRowDef="let row; columns: columns;"
            matRipple 
            tabindex="0" 
            style="cursor: pointer"
            [ngStyle]="{'background-color': selectedRow == row ? 'whitesmoke' : ''}"
            [ngClass]="{'detailRowOpened': tableData.checkExpanded(row)}"
            (click)="tableData.toggleExpanded(row); selectedRow = row;"></mat-row>
        <mat-row *matRowDef="let row; columns: ['expandedRow']" 
            matRipple
            (click)="selectedRow = row;"
            [ngClass]="{'selectedRow': selectedRow == row}"
            (@detailExpand.done)="animation($event)"
            [@detailExpand]="tableData.checkExpanded(row) ? 'expanded' : 'collapsed'"
            style="overflow: hidden"></mat-row>
    </mat-table>

更多

相遇 {{scheduleCourse.Meets} 学期 {{scheduleCourse.Term} 课程名称 {{scheduleCourse.Course} 老师 {{scheduleCourse.Teacher}} 房间 {{scheduleCourse.Room} 参赛日期 {{scheduleCourse.EntryDate.toString()!=junkDate.toString()?scheduleCourse.EntryDate.toLocaleDateString():''}} 删除日期 {{scheduleCourse.DroppedDate.toString()!=junkDate.toString()?scheduleCourse.DroppedDate.toLocaleDateString():''} 团队 {{scheduleCourse.TeamCode} 课程组 {{scheduleCourse.SubjectCode}}-{{scheduleCourse.Section} 在此之后,动画停止工作。此外,我还测试了是否可以创建空洞过渡动画,但该动画也无法播放

现在,我知道tableData工作正常,因为该表显示良好。此外,在从排序触发该事件之前,动画可以完美地工作。事实上,排序工作正常,“detailRow.done”事件即使在动画未播放时也会持续触发。所以,我知道这一定与MatSort和动画交互有关:我只是不知道是什么

以下是我尝试过的:

  • 删除[ngStyle]和[ngClass]
  • 删除表格及其容器上的宽度和高度样式
  • 卸下ngDoCheck生命周期挂钩
  • 更改mat sort标头以使用matColumnDef,并使matColumnDef与排序属性名称匹配
  • 使用setTimeout将排序设置为tableData
  • 在排序更改后,将表“跳出”到DOM中
  • 排序更改后强制在表上显示renderRows
更新1

我试图在stackblitz中重现这个问题,但没有成功。看起来MatSort和Angular动画彼此配合得很好,而且这里还发生了其他一些事情。这给了我一些方向

更新2

所以,我发现了问题,尽管这是一个奇怪的问题。我用几个辅助函数扩展了MatTableDataSource,在这里我得到了“tableData.checkExpanded”和“tableData.toggleExpanded”函数。当我使用组件中的布尔数组检查扩展时,组件工作正常。当我使用这些函数时,我最终遇到了这个问题。这是该类的代码。我可能会更新stackblitz,看看是否可以用这个复制它


导出类TylerMatTableDataSource扩展了MatTableDataSource

我也遇到了同样的问题,通过将动画从

trigger('detailExpand', [
  state('collapsed', style({ height: '0px', minHeight: '0', display: 'none' })),
  state('expanded', style({ height: '*' })),
  transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
])
触发器('detailExpand'[
状态('collapped',样式({height:'0px',minHeight:'0',display:'none'})),
状态('expanded',样式({height:'*'})),
过渡(“展开折叠”,动画(“225ms立方贝塞尔(0.4,0.0,0.2,1)”,
])

触发器('detailExpand'[
状态('collapped,void',style({height:'0px',minHeight:'0',display:'none'})),
状态('expanded',样式({height:'*'})),
过渡(“展开折叠”,动画(“225ms立方贝塞尔(0.4,0.0,0.2,1)”,
过渡(“展开的空心”,动画(“225ms立方贝塞尔(0.4,0.0,0.2,1))
])
只有第一个
状态('collapsed')
状态('collapsed,void'
和最后一个
转换(…)
行的更改

现在,排序和扩展行都可以按预期工作

解决方案归功于pabloFuente。

Angular 10解决方案 export const detailExpand=触发器('detailExpand', [ 状态('折叠,无效',样式({height:'0px'})), 状态('expanded',样式({height:'*'})), 过渡(“展开折叠”,动画(“225ms立方贝塞尔(0.4,0.0,0.2,1)”, 过渡(“展开的空心”,动画(“225ms立方贝塞尔(0.4,0.0,0.2,1)) ]);
使用Flex布局(无“表”、“tr”、“td”元素)时,我无法获得动画效果
@Component({
    selector: 'student-schedule',
    templateUrl: './student-schedule.component.html',
    styleUrls: [
        './student-schedule.component.css'
    ],
    animations: [
        detailExpand
    ]
})
export class StudentScheduleComponent implements OnInit, DoCheck, OnDestroy {

    // Properties
    private _viewOption = 1;
    private _includeDropped = false;
    schedule: ScheduleCourse[] = [];
    subscriptions: Subscription[] = [];
    tableData = new TylerMatTableDataSource();
    junkDate = System.junkDate;
    V10: boolean;
    columns = ['more', 'meets', 'term', 'course', 'teacher', 'room', 'entry date', 'dropped date', 'team'];
    selectedRow: ScheduleCourse;
    expandEmitter = new EventEmitter<boolean>();
    tableHeight: number;
    minTableWidth: number;
    @ViewChild('tableContainer', {read: ElementRef}) tableContainer: ElementRef;
    showProfiles: boolean;
    studentEnrollment: Enrollment;
    _sort: MatSort;

    // Class Functions
    constructor(
        private studentScheduleService: StudentScheduleService,
        private loginService: LoginService,
        private router: Router,
        private dialog: MatDialog,
        private studentService: StudentService,
        private sendEmailService: SendEmailService
    ) { }

    get viewOption(): number {
        return this._viewOption;
    }

    set viewOption(value: number) {
        this._viewOption = value;
        this.getSchedule();
    }

    get includeDropped(): boolean {
        return this._includeDropped;
    }

    set includeDropped(value: boolean) {
        this._includeDropped = value;
        this.checkColumns();
    }

    @ViewChild(MatSort) set sort(value: MatSort) {
        this._sort = value;
        this.tableData.sort = this._sort;
    }

    get sort(): MatSort {
        return this._sort;
    }

    // Event Functions
    ngOnInit() {
        // POST: initializes the data
        this.V10 = this.loginService.LoginSettings.V10;
        this.showProfiles = this.loginService.LoginSettings.ParentPortalCourseScheduleProfiles;
        this.checkColumns();
        this.subscriptions.push(
            this.expandEmitter.subscribe(expand => {
                this.tableData.expandAll(expand);
            }),
            this.studentService.selectedStudentStream$.subscribe(() => {
                this.studentEnrollment = this.studentService.studentEnrollment;
                this.getSchedule();
            })
        );
    }

    ngDoCheck() {
        // POST: determines the height and width of the table container
        if (this.tableContainer) {
            this.tableHeight = System.getTableHeight(this.tableContainer);
        }
    }

    ngOnDestroy() {
        // POST: unsubscribes to all observables
        this.subscriptions.forEach(subscription => {
            subscription.unsubscribe();
        });
    }

    assignmentClick(scheduleCourse: ScheduleCourse) {
        // PRE: the user clicks on an assignment link under a course
        // POST: routes the user to that assignment page
        // TODO: Ensure it links to the proper class
        this.router.navigateByUrl('/student360/assignments');
    }

    attendanceClick(scheduleCourse: ScheduleCourse) {
        // PRE: the user clicks on an attendance link under a course
        // POST: routes the user to that attendance page
        this.router.navigateByUrl('/student360/attendance');
    }

    emailTeacherClick(scheduleCourse: ScheduleCourse) {
        // PRE: the user clicks on an attendance link under a course
        // POST: routes the user to the email page
        // TODO: Ensure it links to the proper teacher
        this.sendEmailService.teacherName = scheduleCourse.TeacherName;
        this.sendEmailService.teacherEmailAddress = scheduleCourse.TeacherEmail;
        this.router.navigateByUrl('/student360/sendEmail');
    }

    gradesClick(scheduleCourse: ScheduleCourse) {
        // PRE: the user clicks on a grade link under a course
        // POST: routes the user to the grade page
        this.router.navigateByUrl('/student360/reportcardgrades');
    }

    courseDescriptionClick(scheduleCourse: ScheduleCourse) {
        // PRE: the user clicks on a course description link under a course
        // POST: shows a modal for the course's description
        this.dialog.open(CourseDescriptionDialogComponent, {
            data: {
                course: scheduleCourse.Course,
                section: scheduleCourse.Section,
                teacherName: scheduleCourse.TeacherName,
                schoolName: scheduleCourse.SchoolName,
                curriculum: scheduleCourse.Curriculum,
                description: scheduleCourse.Description
            }
        });
    }

    classInformationClick(scheduleCourse: ScheduleCourse) {
        // PRE: the user clicks on a class information link under a course
        // POST: shows a modal for that class' profile
        this.dialog.open(ProfileViewerDialogComponent, {
            data: {
                courseSSEC_ID: scheduleCourse.Id,
                courseName: scheduleCourse.Course,
                courseSection: scheduleCourse.Section,
                teacherName: scheduleCourse.TeacherName,
                school: scheduleCourse.SchoolName
            }
        });
    }

    teacherProfileClick(scheduleCourse: ScheduleCourse) {
        // PRE: the user clicks on a teacher profile link under a couse
        // POST: shows a modal for that teacher's profile
        this.dialog.open(ProfileViewerDialogComponent, {
            data: {
                teacherId: scheduleCourse.TeacherId,
                teacherName: scheduleCourse.TeacherName,
                school: scheduleCourse.SchoolName
            }
        });
    }

    animation(event) {
        console.log(event);
    }

    // Methods
    showSchool(scheduleCourse: ScheduleCourse): boolean {
        return this.studentEnrollment.SchoolName &&
            scheduleCourse.SchoolName &&
            this.studentEnrollment.SchoolName.trim().toUpperCase() != scheduleCourse.SchoolName.trim().toUpperCase();
    }

    getSchedule() {
        // POST: obtains the schedule from the server
        this.subscriptions.push(
            this.studentScheduleService.getStudentSchedule(this.viewOption).subscribe(schedule => {
                this.schedule = schedule;
                for (let i = 0; i < this.schedule.length; i++) {
                    this.schedule[i] = System.convert<ScheduleCourse>(this.schedule[i], new ScheduleCourse());
                }
                this.tableData = new TylerMatTableDataSource(this.schedule);
                if (this.sort) {
                    this.tableData.sort = this.sort;
                }
            })
        );
    }

    checkColumns() {
        // POST: checks the columns for ones that shouldn't be there

        // Team is a V9 only column
        if (this.V10 && this.columns.includes('team')) {
            this.columns.splice(this.columns.indexOf('team'), 1);
        } else if (!this.V10 && !this.columns.includes('team')) {
            this.columns.push('team');  // Team is always on the end
        }

        // Entry date and dropped date are only there if include dropped
        if (this.includeDropped) {
            if (!this.columns.includes('entry date')) {
                this.columns.splice(5, 0, 'entry date');
            }
            if (!this.columns.includes('dropped date')) {
                this.columns.splice(6, 0, 'dropped date');
            }
            this.minTableWidth = 1000;
        } else {
            if (this.columns.includes('dropped date')) {
                this.columns.splice(this.columns.indexOf('dropped date'), 1);
            }
            if (this.columns.includes('entry date')) {
                this.columns.splice(this.columns.indexOf('entry date'), 1);
            }
            this.minTableWidth = 750;
        }
    }
}
export class TylerMatTableDataSource extends MatTableDataSource<any>{
    filterNumber:number = 0;
    filterTestValue:string = '';
    filters:FilterModel[] = [];

    expandedElements:number[] = [];

    constructor(initialData?: any[]){
        super(initialData);
        this.filterPredicate = this.genericFilter;
    }    

    toggleExpanded(row: any) {
        if (row != undefined) {

            if(row.detailRow == undefined || row.detailRow == false){
                row.detailRow = true;
            }
            else{
                row.detailRow = false;
            }
        }
    }

    checkExpanded(row:any):boolean{
        if(row.detailRow == undefined){
            row.detailRow = false;
        }

        return row.detailRow;
    }

    expandAll(expand: boolean) {
        this.data.forEach(element => {
            element.detailRow = expand;
        });
    }
}
trigger('detailExpand', [
  state('collapsed', style({ height: '0px', minHeight: '0', display: 'none' })),
  state('expanded', style({ height: '*' })),
  transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
])
trigger('detailExpand', [
  state('collapsed, void', style({ height: '0px', minHeight: '0', display: 'none' })),
  state('expanded', style({ height: '*' })),
  transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
  transition('expanded <=> void', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)'))
])
export const detailExpand = trigger('detailExpand',
    [
        state('collapsed, void', style({ height: '0px'})),
        state('expanded', style({ height: '*' })),
        transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
        transition('expanded <=> void', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)'))
    ]);
  > mat-row.detail-row {
    overflow: hidden;
    border-style: none;
    min-height: auto;
    &.detail-row-collapsed {
      max-height: 0;
      transition: max-height .4s ease-out;
    }
    &.detail-row-expanded {
      max-height: 1000px;
      transition: max-height .4s ease-in;
    }
  }