- Blazor_Academy.Admin 으로 프로젝트 새로 생성(관리자 페이지)
- bootstrap SB Admin2 템플릿은
Start Bootstrap 사이트에 접속해서 다운로드 혹은 구글에서 "bootstrap template"으로 검색 해서 사이트 접속.
- 기존 modern Clone과 동일하게 layout에 SB Admin2 페이지 레이아웃 코드 및 공통 js 파일 link 추가.
- 각페이지를 .razor 파일로 clone
- modern 페이지와 내용이 동일하여 차이점인 chart가 안나오던 부분을 어떻게 처리했는지만 정리하려고 함.
1. Admin 페이지 clone
- 페이지를 clone을 완료하면 1번 이미지와 같이 차트 부분이 표시되지 않고
F12
번을 눌러 consol을 확인 하면 에러가 뜨는것을볼 수 있다.
chart-area-demo.js
파일에서 area차트를 보여주는 코드가 작성되어 있는데
id = myAreaChart
를 가진 canvas에 chart를 뿌려주는 코드인데 해당 스크립트에서
.razor로 작성된 페이지의 객체 id를 검색하지 못해 에러가 발생하며 차트가 표시되지 않음.
- 기존에는 javascript에서 특정 id를 가진 객체를 찾아 chart를 표시해 주었지만
변경한 내용은 해당 blazor에서 호출할 수 있는 펑션을 만들어 페이지 로드시 해당 함수를 호출하여 chart를 표출 하도록 변경
- clone후 chart.js 수정전 페이지
- chart.js 수정후 페이지
1) javascript 수정.
//기존소스
라고 되어있는 부분이 원래 기존 소스로 특정 id를 찾아 chart를 뿌려주는 방식이였음.
- 변경점은
window.BLazorChart_area_demo
함수를 id를 받아 chart를 뿌려줄수 있도록 생성. BLazorChart_area_demo
는 실제 razor페이지에서 호출하는 함수명과 동일.
- 기존소스를
window.BLazorChart_area_demo
내부에 복사 붙여넣기 하여 구현.
- 기존 소스를 주석처리한 이유는 주석처리 하지 않을경우 javasript를 호출할 때 해당 구문이 실행되어 에러가 발생하기 떄문.
Chart.defaults.global.defaultFontFamily = 'Nunito', '-apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif';
Chart.defaults.global.defaultFontColor = '#858796';
function number_format(number, decimals, dec_point, thousands_sep) {
number = (number + '').replace(',', '').replace(' ', '');
var n = !isFinite(+number) ? 0 : +number,
prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
s = '',
toFixedFix = function(n, prec) {
var k = Math.pow(10, prec);
return '' + Math.round(n * k) / k;
};
s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
if (s[0].length > 3) {
s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
}
if ((s[1] || '').length < prec) {
s[1] = s[1] || '';
s[1] += new Array(prec - s[1].length + 1).join('0');
}
return s.join(dec);
}
window.BLazorChart_area_demo = function (id) {
var ctx = document.getElementById(id);
var myLineChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
datasets: [{
label: "Earnings",
lineTension: 0.3,
backgroundColor: "rgba(78, 115, 223, 0.05)",
borderColor: "rgba(78, 115, 223, 1)",
pointRadius: 3,
pointBackgroundColor: "rgba(78, 115, 223, 1)",
pointBorderColor: "rgba(78, 115, 223, 1)",
pointHoverRadius: 3,
pointHoverBackgroundColor: "rgba(78, 115, 223, 1)",
pointHoverBorderColor: "rgba(78, 115, 223, 1)",
pointHitRadius: 10,
pointBorderWidth: 2,
data: [0, 10000, 5000, 15000, 10000, 20000, 15000, 25000, 20000, 30000, 25000, 40000],
}],
},
options: {
maintainAspectRatio: false,
layout: {
padding: {
left: 10,
right: 25,
top: 25,
bottom: 0
}
},
scales: {
xAxes: [{
time: {
unit: 'date'
},
gridLines: {
display: false,
drawBorder: false
},
ticks: {
maxTicksLimit: 7
}
}],
yAxes: [{
ticks: {
maxTicksLimit: 5,
padding: 10,
callback: function (value, index, values) {
return '$' + number_format(value);
}
},
gridLines: {
color: "rgb(234, 236, 244)",
zeroLineColor: "rgb(234, 236, 244)",
drawBorder: false,
borderDash: [2],
zeroLineBorderDash: [2]
}
}],
},
legend: {
display: false
},
tooltips: {
backgroundColor: "rgb(255,255,255)",
bodyFontColor: "#858796",
titleMarginBottom: 10,
titleFontColor: '#6e707e',
titleFontSize: 14,
borderColor: '#dddfeb',
borderWidth: 1,
xPadding: 15,
yPadding: 15,
displayColors: false,
intersect: false,
mode: 'index',
caretPadding: 10,
callbacks: {
label: function (tooltipItem, chart) {
var datasetLabel = chart.datasets[tooltipItem.datasetIndex].label || '';
return datasetLabel + ': $' + number_format(tooltipItem.yLabel);
}
}
}
}
});
}
2) .razor 페이지 수정
- 코드 비하인드 방식으로 .cs 파일을 추가하여 코드부분을 분리하는 방법도 있지만 .razor 페이지 내부에서 @code 부분에 구현.
@inject IJSRuntime js
razor페이지에서 javascript를 사용할 수 있도록 jsruntime을 주입받음.
protected override async Task OnAfterRenderAsync(bool firstRender)
함수에 javascript에서 정의한 함수를 호출
해당 함수를 사용한 이유는 rendering이 되기전에 함수를 호출 하게 되면 에러가 발생하기 때문이며 매개변수 firstRender
는 처음 load할때만 true값이 들어와서 처음 로드시에만 호출.
await js.InvokeAsync<object>("BLazorChart_area_demo", "myAreaChart")
함수 의 첫번째 매개변수 = javascript에서 만든 함수명, 두번째 변수는 chart를 표시할 canvas의 id를 넣어줌.
@page "/"
@page "/Index"
@inject IJSRuntime js;
<div>
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">Dashboard</h1>
<a href="#" class="d-none d-sm-inline-block btn btn-sm btn-primary shadow-sm">
<i class="fas fa-download fa-sm text-white-50"></i> Generate Report
</a>
</div>
<div class="row">
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-primary shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">
Earnings (Monthly)
</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">$40,000</div>
</div>
<div class="col-auto">
<i class="fas fa-calendar fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-success shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">
Earnings (Annual)
</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">$215,000</div>
</div>
<div class="col-auto">
<i class="fas fa-dollar-sign fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-info shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">
Tasks
</div>
<div class="row no-gutters align-items-center">
<div class="col-auto">
<div class="h5 mb-0 mr-3 font-weight-bold text-gray-800">50%</div>
</div>
<div class="col">
<div class="progress progress-sm mr-2">
<div class="progress-bar bg-info" role="progressbar"
style="width: 50%" aria-valuenow="50" aria-valuemin="0"
aria-valuemax="100"></div>
</div>
</div>
</div>
</div>
<div class="col-auto">
<i class="fas fa-clipboard-list fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-warning shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">
Pending Requests
</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">18</div>
</div>
<div class="col-auto">
<i class="fas fa-comments fa-2x text-gray-300"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xl-8 col-lg-7">
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-primary">Earnings Overview</h6>
<div class="dropdown no-arrow">
<a class="dropdown-toggle" href="#" role="button" id="dropdownMenuLink"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-ellipsis-v fa-sm fa-fw text-gray-400"></i>
</a>
<div class="dropdown-menu dropdown-menu-right shadow animated--fade-in"
aria-labelledby="dropdownMenuLink">
<div class="dropdown-header">Dropdown Header:</div>
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</div>
</div>
<div class="card-body">
<div class="chart-area">
<canvas id="myAreaChart"></canvas>
</div>
</div>
</div>
</div>
<div class="col-xl-4 col-lg-5">
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-primary">Revenue Sources</h6>
<div class="dropdown no-arrow">
<a class="dropdown-toggle" href="#" role="button" id="dropdownMenuLink"
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-ellipsis-v fa-sm fa-fw text-gray-400"></i>
</a>
<div class="dropdown-menu dropdown-menu-right shadow animated--fade-in"
aria-labelledby="dropdownMenuLink">
<div class="dropdown-header">Dropdown Header:</div>
<a class="dropdown-item" href="#">Action</a>
<a class="dropdown-item" href="#">Another action</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">Something else here</a>
</div>
</div>
</div>
<div class="card-body">
<div class="chart-pie pt-4 pb-2">
<canvas id="myPieChart"></canvas>
</div>
<div class="mt-4 text-center small">
<span class="mr-2">
<i class="fas fa-circle text-primary"></i> Direct
</span>
<span class="mr-2">
<i class="fas fa-circle text-success"></i> Social
</span>
<span class="mr-2">
<i class="fas fa-circle text-info"></i> Referral
</span>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 mb-4">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Projects</h6>
</div>
<div class="card-body">
<h4 class="small font-weight-bold">
Server Migration <span class="float-right">20%</span>
</h4>
<div class="progress mb-4">
<div class="progress-bar bg-danger" role="progressbar" style="width: 20%"
aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<h4 class="small font-weight-bold">
Sales Tracking <span class="float-right">40%</span>
</h4>
<div class="progress mb-4">
<div class="progress-bar bg-warning" role="progressbar" style="width: 40%"
aria-valuenow="40" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<h4 class="small font-weight-bold">
Customer Database <span class="float-right">60%</span>
</h4>
<div class="progress mb-4">
<div class="progress-bar" role="progressbar" style="width: 60%"
aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<h4 class="small font-weight-bold">
Payout Details <span class="float-right">80%</span>
</h4>
<div class="progress mb-4">
<div class="progress-bar bg-info" role="progressbar" style="width: 80%"
aria-valuenow="80" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<h4 class="small font-weight-bold">
Account Setup <span class="float-right">Complete!</span>
</h4>
<div class="progress">
<div class="progress-bar bg-success" role="progressbar" style="width: 100%"
aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 mb-4">
<div class="card bg-primary text-white shadow">
<div class="card-body">
Primary
<div class="text-white-50 small">#4e73df</div>
</div>
</div>
</div>
<div class="col-lg-6 mb-4">
<div class="card bg-success text-white shadow">
<div class="card-body">
Success
<div class="text-white-50 small">#1cc88a</div>
</div>
</div>
</div>
<div class="col-lg-6 mb-4">
<div class="card bg-info text-white shadow">
<div class="card-body">
Info
<div class="text-white-50 small">#36b9cc</div>
</div>
</div>
</div>
<div class="col-lg-6 mb-4">
<div class="card bg-warning text-white shadow">
<div class="card-body">
Warning
<div class="text-white-50 small">#f6c23e</div>
</div>
</div>
</div>
<div class="col-lg-6 mb-4">
<div class="card bg-danger text-white shadow">
<div class="card-body">
Danger
<div class="text-white-50 small">#e74a3b</div>
</div>
</div>
</div>
<div class="col-lg-6 mb-4">
<div class="card bg-secondary text-white shadow">
<div class="card-body">
Secondary
<div class="text-white-50 small">#858796</div>
</div>
</div>
</div>
<div class="col-lg-6 mb-4">
<div class="card bg-light text-black shadow">
<div class="card-body">
Light
<div class="text-black-50 small">#f8f9fc</div>
</div>
</div>
</div>
<div class="col-lg-6 mb-4">
<div class="card bg-dark text-white shadow">
<div class="card-body">
Dark
<div class="text-white-50 small">#5a5c69</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-6 mb-4">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Illustrations</h6>
</div>
<div class="card-body">
<div class="text-center">
<img class="img-fluid px-3 px-sm-4 mt-3 mb-4" style="width: 25rem;"
src="img/undraw_posting_photo.svg" alt="...">
</div>
<p>
Add some quality, svg illustrations to your project courtesy of <a target="_blank" rel="nofollow" href="https://undraw.co/">unDraw</a>, a
constantly updated collection of beautiful svg images that you can use
completely free and without attribution!
</p>
<a target="_blank" rel="nofollow" href="https://undraw.co/">
Browse Illustrations on
unDraw →
</a>
</div>
</div>
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Development Approach</h6>
</div>
<div class="card-body">
<p>
SB Admin 2 makes extensive use of Bootstrap 4 utility classes in order to reduce
CSS bloat and poor page performance. Custom CSS classes are used to create
custom components and custom utility classes.
</p>
<p class="mb-0">
Before working with this theme, you should become familiar with the
Bootstrap framework, especially the utility classes.
</p>
</div>
</div>
</div>
</div>
</div>
@code
{
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await js.InvokeAsync<object>("BLazorChart_area_demo", "myAreaChart");
await js.InvokeAsync<object>("BLazorChart_pie_demo", "myPieChart");
}
}
}
3) 정리하기
- 나머지 차트들도 위와 동일한 방식으로 적용 하면 페이지에 차트가 표시됨.
- github 깃허브 경로에 resouce 폴더 내부에 SB Admin2 샘플 페이지 프로젝트 파일있음.(혹시 bootstrap페이지에서 소스파일이 없어질 경우 해당 파일 사용 하면됨.)
- 현재까지 진행 상태를
05_Admin_Page_clone
브랜치에 저장되어 있음.