확인 내용

* procedure가 올바른 plan을 선정하였는지

* cached plan 사이즈가 효율적으로 사용되고 있는지

* plan에 해당하는 query확인

 

 

Procedure query_id 및 plan_id 확인

하나의 SP에 여러 plan이 존재할수 있음 

그중 비효율적인 plan을 사용하는 경우 이슈가 발생

--'spTest_get' SP를 분석

select qsq.query_id, qp.plan_id, qst.query_sql_text, qp.query_plan 
from sys.query_store_query qsq
inner join sys.query_store_query_text qst
	on qsq.query_text_id = qst.query_text_id
inner join sys.query_store_plan qp
	on qp.query_id = qsq.query_id
where object_id = object_id('spTest_get')

/**
query_id	plan_id
5324	662
5324	782
5324	787
5324	807
5324	818

3315	663
8092	1200
8093	1201
**/

Cached plan 카운트 및 사이즈

adhoc 쿼리를 과도하게 호출 시 cached plan이 저장 가능한 공간을 초과 할 수 있음.

아래의 경우 1회만 사용되는 plan이 대부분인 케이스

1회만 사용되는 plan을 sp_executesql 형태로 변경하거나 동일 plan을 사용하도록 유도가 필요함

-- 모든 cached plan을 조회

select objtype, count(*) as cnt, sum(convert(bigint, size_in_bytes))/(1024*1024) as size_mb
from sys.dm_exec_cached_plans
--where usecounts = 1
group by objtype
order by cnt desc

/**
objtype	cnt	size_mb
Adhoc	156329	6906			<--
Prepared	3708	967
View	1285	181
Proc	215	253
**/


-- cached plan 사용 횟수가 1회 경우만 조회
select objtype, count(*) as cnt, sum(convert(bigint, size_in_bytes))/(1024*1024) as size_mb
from sys.dm_exec_cached_plans
where usecounts = 1
group by objtype
order by cnt desc

/**
objtype	cnt	size_mb
Adhoc	153763	1889			<--
Prepared	3145	242
Proc	41	19
**/

query_hash 통한 이슈 query 확인

이슈 query 확인 후 수정

-- 실행 count가 1인 경우만 조회

select query_hash, count(*) as cnt
from sys.dm_exec_query_stats
where execution_count = 1
group by query_hash
order by cnt desc

/**
query_hash	cnt
0xD1CC06V33C082806	135420      <--- 확인
0x3129830A6071HND2	2108
0x30E30C19A239CX39	218
0x690CY4EC5110G475	218
~
**/


-- 이슈 query text 확인

select top 100 st.text
from sys.dm_exec_query_stats qs
cross apply sys.dm_exec_sql_text (qs.sql_handle) as st
where qs.query_hash = 0xD1CC06V33C082806 

/**
USE master   INSERT INTO *******************
USE master   INSERT INTO *******************
**/

설명

textarea에 image 및 tag 적용이 되지 않음

적용이 불가능하지는 않은듯하나 예외 사항이 많아 적용이 쉽지 않음

div contentEditable="true" 형태를 사용하여 image 및 text를 함께 작성

div contentEditable 형태는 form 형태로 전달하기 힘들기 때문에 hidden text를 사용하여 db에 내용을 전달

Page를 불러 올때 hidden 형태의 타입의 값을 div contentEditable 부분에 가져와서 보여줌

 

html

DB 에서 데이터를 조회하여 templates에 result.content_detail 값을 전달하여 처리하는 형태

content_detail은 hidden형태로 값을 전달하는 중간 역할

content_detail_edit는 실제로 데이터(이미지 및 텍스트)를 화면에 출력하는 역할

 

content_detail_image는 file 타입으로 이미지의 정보를 처리

content_img_width는 이미지의 width정보를 전달하기 위한 용도

<div class="form-group col-md-10">
    <div class="input-group">
        <div class="input-group-prepend iw-100" data-toggle="tooltip" data-container="body" title="content 내용">
            <button type="button" class="btn btn-warning iw-100" onclick="expandText()">Content *</button>
        </div>
        <input type="hidden" name="content_detail" id="content_detail" value = "{{ result.content_detail }}"/>
        <div contentEditable="true" style="min-height: 100px;height: 100%;max-width: 100%;white-space: pre-wrap; word-break: break-word;"class="form-control" id="content_detail_edit" required> </div>
    </div>
</div>

<div class="form-group col-md-2">
    <div class="input-group">
        <div class="custom-file">
            <input type="file" class="custom-file-input" id="content_detail_image" name="content_detail_image">
            <label class="custom-file-label" for="content_detail_image">Choose file</label>
        </div>
        <div class="input-group-append">
            <span class="input-group-text" onclick="goContent_Detail_Image();">Upload</span>
        </div>
    </div>
    Image Width <input style="width: 75px;" type="text" name="content_img_width" class="form-control"  id="content_img_width" value="800" />
</div>

javascript

function 내부에서 bsCustomFileInput.init();를 호출하여 파일 객체 초기화 진행

function 내부에서 hidden type의 데이터를 contentDeitable 타입의 값에 복사하여 page에 출력시 사용

 

goContent_Detail_Image함수에서

file객체의 정보를 ajax 형태로 python으로 전달

python으로 서버에 이미지 생성 성공시 저장된 위치 및 파일명을 기반으로, templates의 content_detail 및 content_detail_edit에 이미치 tag를 추가하여 출력

이미지는 content_img_width에 설정된 값으로 생성(너무 크게 생성되는것을 방지하기 위해 1024를 max로 처리)

 

goContent_Edit함수에서

content_detail_edit에 수정한 내용을 DB에 저장할 때는 content_detail로 값을 변경하여 전달

<!-- Tempusdominus Bootstrap 4 -->
<link rel="stylesheet" href="/static/plugins/tempusdominus-bootstrap-4/css/tempusdominus-bootstrap-4.min.css">

<!-- jQuery -->
<script src="/static/plugins/jquery/jquery.min.js"></script>
<!-- Bootstrap 4 -->
<script src="/static/plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
<!-- bs-custom-file-input -->
<script src="/static/plugins/bs-custom-file-input/bs-custom-file-input.min.js"></script>


$(function () {
    // File 객체 초기화
    bsCustomFileInput.init();
  
    // hidden type의 데이터를 contentEditable 타입의 값에 복사
    Src_Val = $('#content_detail').val()
    document.getElementById('content_detail_edit').innerHTML = Src_Val;
    document.getElementById("content_detail_edit").focus();
});


function goContent_Detail_Image(){
    
    let content_id = $('#content_id').val()
    let file = $('#content_detail_image')[0].files[0]

    let form_data = new FormData()
    form_data.append("id_give", content_id)
    form_data.append("file_give", file)

    // Image를 서버에 저장
    $.ajax({
        url:'/content/detail_image',
        type:'POST',
        data: form_data,
        cache: false,
        contentType: false,
        processData: false,
        success: function(data){
            showAlert('content detail image insert successful!!!', data.filename, 2, 2000);

            // 이미지 최대 width는 1024로 제한
            img_width = $('#content_img_width').val()
            if ( img_width > 1024 ) img_width = 1024;

            Src_Val = $('#content_detail').val()
            Change_Val = Src_Val + '<br> <img width = "' + img_width + '" src="/static/img/content/' + data.filename + '">' 

            document.getElementById('content_detail').innerHTML = Change_Val;
            document.getElementById('content_detail_edit').innerHTML = Change_Val;

        },
        error: function(xhr, ajaxOptions, thrownError) {
            var fail_title = "content detail img upload Failed!!";
            var fail_msg = '';
            var err_msg = JSON.parse(xhr.responseText);

            if (thrownError == 'UNKNOWN'){
                fail_msg = err_msg.message + ' (' + xhr.status + ')'
            } else {
                fail_msg = err_msg.message + ' (' + thrownError + ',' + xhr.status + ')';
            }

            showAlert(fail_title,fail_msg,3,1500);
        }
    });
    return false;
}


function goContent_Edit(){
// ** Content 수정 ** //

    // content detail text 전환
    Src_Val = document.getElementById('content_detail_edit').innerHTML;
    $('#content_detail').val(Src_Val);

    var param = $('#content_form').serialize();

    // content 수정 요청
    $.ajax({
        url:'/content/edit',
        type:'POST',
        data: param,
        success: function(data){
            showAlert('content Edit Successful!!!', data.message, 2, 2000);
        },
        error: function(xhr, ajaxOptions, thrownError) {
            var fail_title = "content edit Failed!!";
            var fail_msg = '';
            var err_msg = JSON.parse(xhr.responseText);

            if (thrownError == 'UNKNOWN'){
                fail_msg = err_msg.message + ' (' + xhr.status + ')'
            } else {
                fail_msg = err_msg.message + ' (' + thrownError + ',' + xhr.status + ')';
            }

            showAlert(fail_title,fail_msg,3,1500);
        }
    });
    return false;
};

python

이미지 파일명은 content_id + prefix_date + filename을 조합하여, 이미지 content별 구분과 등록 순서를 확인 가능하도록 네이밍 처리함

이미지를 서버에 저장한 이후 화면에 이미지를 출력하기 위해 파일명을 리턴

from werkzeug import secure_filename

@bts_blueprint.route('content/detail_image', methods=['POST'])
def content_detail_image():

    print("##  content_detail_image  ##")

    try:
        file = request.files['file_give']
        content_id = request.form['id_give']
        #extention = file.filename.split('.')[-1]

        UploadPath = 'static/img/content/'
        
        now_date = datetime.datetime.now()
        prefix_date = now_date.strftime('_%Y%m%d_%H%M%S_')
        
        ImgFullname = UploadPath + content_id + prefix_date + file.filename
        
        #file.save(UploadPath, secure_filename(file.filename))
        file.save(ImgFullname)

        return jsonify(filename=content_id + prefix_date + file.filename), 200

    except (RuntimeError, TypeError, NameError, SQLAlchemyError) as e:
        print("#### content detail img upload Failed !! " + str(e))

        return jsonify(message=str(e)), 1002

참고

https://velog.io/@wrs0707/%EB%82%98%ED%99%80%EB%A1%9C%EC%9D%BC%EA%B8%B0%EC%9E%A5-%EB%A7%8C%EB%93%A4%EA%B8%B0

 

https://blog.naver.com/ynskoh/221064660966

Linux chkconfig

chkconfig는 리눅스서버가 부팅될 때 실행될 프로그램을 설정하는 역활

서버 부팅시 프로그램 리스트와 런레벨별 실행여부 등을 확인하거나 설정

런레벨

레벨 상세 정보
0 종료
1 단일 사용자 모드
2 사용하지 않음
3 다중 사용자 모드 (CUI) - 터미널
4 사용하지 않음
5 다중 사용자 모드 (GUI) - X윈도우
6 재부팅

명령어

// 리스트 확인
  chkconfig --list [프로그램명]

// Linux 시작시 실행되도록 설정
  chkconfig [프로그램명] on

// Linux 시작시 실행되지 않도록 설정
  chkconfig [프로그램명] off

// Linux 런 레벨별로 시작시 실행되도록 설정
  chkconfig --level [레벨] [프로그램명] on

// Linux 런 레벨별로 시작시 실행되지 않도록 설정
  chkconfig --level [레벨] [프로그램명] off

참고

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=diceworld&logNo=220295874000

기능

- 라인 다중 차트(line multiple chart)

- 다중 차트 클릭 이벤트 제어(multiple chart click event control)

- 상시 레이블 출력( show label without mouseover)

html

canvas에 cMultiLineChart 생성

<canvas id="cMultiLineChart" style="min-height: 400px; height: 400px; max-height: 1000px; width: 700px; max-width: 100%;"></canvas>

javascript

chart.min.js : chart.js 3.x 사용을 위해 추가

chartjs-plugin-datalabels@2.0.0 : 마우스 오버없이 레이블을 항상 표시하기 위해 추가 ( chart.js 3.x 버전 부터는 2.0.0 사용)

 

[상시 레이블 출력 로직]

chart.js 3.x 버전에서 datalabels을 사용하기 위해서 반드시 chart config에 plugins: [ChartDataLabels] 추가 (중요)

plugins: [ChartDataLabels]를 추가하지 않는 경우, CDN을 추가하더라도 datalabels 인식이 되지 않음

 

data의 labels를 외부에서 입력 하는 경우

MultiLineChart.data.labels.push('월');

MultiLineChart.data.labels.push('화');

...

MultiLineChart.data.labels.push('일');

MultiLineChart.update();

 

group별 datasets의 data는 일반적으로 외부(DB)에서 조회 후 아래 형태로 입력

Chart_config.data.datasets[0].data.push(11);

Chart_config.data.datasets[0].data.push(22);

Chart_config.data.datasets[1].data.push(33);

Chart_config.data.datasets[1].data.push(44);

Chart_config.data.datasets[2].data.push(55);

Chart_config.data.datasets[2].data.push(66);

MultiLineChart.update();

 

datasets의 datalabels Color는 function을 사용하여 ctx값 추출하여 처리

예제는 value가 5 이상인 경우만 Color를 적용

function(ctx) {
    var value = ctx.dataset.data[ctx.dataIndex];
    return value > 5 ? ctx.dataset.backgroundColor : null;
}

color : label의 색상

backgroundColor : label 배경 색상

formatter : label 출력값

 

[클릭 이벤트 로직]

이벤트 발생 정보 조회

chart.js 3.x 이후 버전부터 getElementsAtEventForMode를 사용

var activePoints = C_Issue_analysis_range.getElementsAtEventForMode(evt, 'point', C_Issue_analysis_range.options);

chart.js 공식 가이드

 

클릭한 Chart의 그룹 인덱스 정보 ( 예제 에서는 Group 1, Group 2, Group 3 의 인덱스)
var datasetIndex = activePoints[0].datasetIndex

 

클릭한 그룹의 Value를 참조하기 위한 Index
var index = activePoints[0].index

 

Group label 정보

var label = MultiLineChart.data.datasets[datasetIndex].label;

 

클릭한 x좌표의 값 정보

var xlavel = MultiLineChart.scales["x"]._labelItems[index].label;

 

클릭한 Group의 Value값

var value = MultiLineChart.data.datasets[datasetIndex].data[index];

<script src="https://cdn.jsdelivr.net/npm/chart.js@3.0.0/dist/chart.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@2.0.0"></script>

// Chart ctx Create
var Chart_ctx = document.getElementById('cMultiLineChart').getContext('2d');

// Chart Config Set
Chart_config = {
    plugins: [ChartDataLabels],
    type: 'line',
    data: {
        labels: ['월','화','수','목','금','토','일'],
        datasets: [
            {
                label: 'Group 1',
                data: [1,1,2,2,3,3,4],
                backgroundColor: 'rgba(255, 99, 132, 0.2)',
                borderColor: 'rgba(255, 99, 132, 1)',
                borderWidth: 3,
                datalabels: {
                    labels: {
                        value: {
                            align: 'top',
                            backgroundColor: function(ctx) {
                                var value = ctx.dataset.data[ctx.dataIndex];
                                return value > 0 ? ctx.dataset.backgroundColor : null;
                            },
                            borderWidth: 2,
                            borderRadius: 4,
                            color: function(ctx) {
                                var value = ctx.dataset.data[ctx.dataIndex];
                                return value > 0 ? 'black' : null;
                            },
                            padding: 4
                        }
                    }
                }
            },
            {
                label: 'Group 2',
                data: [4,4,5,5,0,0,1],
                backgroundColor: 'rgba(54, 162, 235, 0.2)',
                borderColor: 'rgba(54, 162, 235, 1)',
                borderWidth: 3,
                datalabels: {
                    labels: {
                        value: {
                            align: 'top',
                            backgroundColor: function(ctx) {
                                var value = ctx.dataset.data[ctx.dataIndex];
                                return value > 0 ? ctx.dataset.backgroundColor : null;
                            },
                            borderWidth: 2,
                            borderRadius: 4,
                            color: function(ctx) {
                                var value = ctx.dataset.data[ctx.dataIndex];
                                return value > 0 ? 'black' : null;
                            },
                            padding: 4
                        }
                    }
                }
            },
            {
                label: 'Group 3',
                data: [0,0,7,8,9,4,1],
                backgroundColor: 'rgba(50, 220, 20, 0.2)',
                borderColor: 'rgba(50, 220, 20, 1)',
                borderWidth: 3,
                datalabels: {
                    labels: {
                        value: {
                            align: 'top',
                            backgroundColor: function(ctx) {
                                var value = ctx.dataset.data[ctx.dataIndex];
                                return value > 5 ? ctx.dataset.backgroundColor : null;
                            },
                            borderWidth: 2,
                            borderRadius: 4,
                            color: function(ctx) {
                                var value = ctx.dataset.data[ctx.dataIndex];
                                return value > 5 ? 'black' : null;
                            },
                            padding: 4
                        }
                    }
                }
            }
        ]
    },
    options: {
        responsive: true,
        scales: {
            y: { 
                beginAtZero: true
            }
        },
        plugins: {
            legend: {
                position: 'left',
            },
            title: {
                display: true,
                text: '그룹별 주간 카운트',
                font: {
                    size: 30
                }
            }
       }
    }
};

// Chart Create
var MultiLineChart = new Chart(Chart_ctx, Chart_config);

// Chart Click Event
document.getElementById("cMultiLineChart").onclick = function(evt) {

    var activePoints = MultiLineChart.getElementsAtEventForMode(evt, 'point', MultiLineChart.options);

    var datasetIndex = activePoints[0].datasetIndex
    var index = activePoints[0].index

    var label = C_Issue_analysis_interval.data.datasets[datasetIndex].label;
    var xlavel = C_Issue_analysis_interval.scales["x"]._labelItems[index].label
    var value = C_Issue_analysis_interval.data.datasets[datasetIndex].data[index]
    
    //alert(label + "  :  " + value + "  :  " + xlavel);

    //Process
};

참고

https://v2_0_0--chartjs-plugin-datalabels.netlify.app/guide/getting-started.html#installation

 

Getting Started | chartjs-plugin-datalabels

Getting Started Installation npm (opens new window) (opens new window) CDN (opens new window) (opens new window) By default, https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels returns the latest (minified) version, however it's highly recommended (open

v2_0_0--chartjs-plugin-datalabels.netlify.app

https://www.chartjs.org/docs/3.2.1/samples/tooltip/interactions.html

https://www.chartjs.org/docs/latest/getting-started/v3-migration.html

https://www.chartjs.org/docs/latest/developers/api.html

https://stackoverflow.com/questions/50515985/get-ylabel-value-onclick-chart-js

현재 Page에서 열기

var OpenURL = 'https://openurl.co.kr';

window.location = OpenURL;

새탭에서 Page 열기

var OpenURL = 'https://openurl.co.kr';

window.open(OpenURL);

<a>~</a> 태그의 속성

_blank: 새 탭, 창

_self: 현재 탭, 창

_parent: 부모 탭, 창
_top: 최상위 탭, 창

<a href="http://tistory.com/" target="_blank">새 창, 탭 열기</a>
<a href="http://tistory.com/" target="_self">현재 창, 탭 열기</a>
<a href="http://tistory.com/" target="_parent">부모 탭, 창</a>
<a href="http://tistory.com/" target="_top">최상위 탭, 창</a>

문제 내용

1.txt를 2.txt로 복사하려고 하는데 overwrite 하겠냐고 물어본다.

수작업의 경우에는 y를 눌러주면 되지만, 스크립트를 통한 작업의 경우에는 키보드 입력을 요구하므로 스크립트 실행이 중단되는 문제가 있다.

[root@zetawiki ~]# echo hello > 1.txt
[root@zetawiki ~]# echo world > 2.txt
[root@zetawiki ~]# cp 1.txt 2.txt
cp: overwrite `2.txt'?

원인

cp에 대한 alias로 -i 옵션[1]이 있어서 그렇다

[root@zetawiki ~]# alias | grep cp
alias cp='cp -i'

해결 방법

방법 1) 아래와 같이 cp 앞에 역슬래시를 넣으면 alias 대신 원래의 명령어가 실행된다.
[root@zetawiki ~]# \cp 1.txt 2.txt
[root@zetawiki ~]# cat 2.txt
hello


방법 2) 전체경로 사용
[root@zetawiki ~]# /bin/cp 1.txt 2.txt
[root@zetawiki ~]# cat 2.txt
hello

참고

https://zetawiki.com/wiki/CentOS_cp_%EA%B0%95%EC%A0%9C%EB%A1%9C_%EB%8D%AE%EC%96%B4%EC%93%B0%EA%B8%B0

today, tomorrow 날짜구하기

const today = new Date();
const tomorrow = new Date();

// Add 1 Day
tomorrow.setDate(today.getDate() + 1);

입력한 날짜(yyyyMMdd)가 유효한 날짜인지 검사

function is_valid_date(date_str)
{
    var yyyyMMdd = String(date_str);
    var year = yyyyMMdd.substring(0,4);
    var month = yyyyMMdd.substring(4,6);
    var day = yyyyMMdd.substring(6,8);

    if (!is_number(date_str) || date_str.length!=8)
        return false;

    if (Number(month)>12 || Number(month)<1)
        return false;

    if (Number(last_day(date_str))<day)
        return false;

    return true;
}

yyyy-MM-dd 날짜 문자열을 Date형으로 반환

function to_date2(date_str)
{
    var yyyyMMdd = String(date_str);
    var sYear = yyyyMMdd.substring(0,4);
    var sMonth = yyyyMMdd.substring(5,7);
    var sDate = yyyyMMdd.substring(8,10);

    //alert("sYear :"+sYear +"   sMonth :"+sMonth + "   sDate :"+sDate);
    return new Date(Number(sYear), Number(sMonth)-1, Number(sDate));
}

Date형을 yyyyMMdd형의 문자열로 변환

function get_date_str(date)
{
    var sYear = date.getFullYear();
    var sMonth = date.getMonth() + 1;
    var sDate = date.getDate();

    sMonth = sMonth > 9 ? sMonth : "0" + sMonth;
    sDate  = sDate > 9 ? sDate : "0" + sDate;
    return sYear + sMonth + sDate;
}

주어진 날짜가 윤년인지를 검사

function is_leap_year(date_str)
{
    var year = date_str.substring(0,4);
    if (year%4 == 0)
    {
        if (year%100 == 0)
            return (year%400 == 0);
        else
            return true;
    }
    else
        return false;
}

주어진 날짜(yyyyMMdd, yyyyMM) 그 달의 마지막 날짜를 반환

function last_day(date_str)
{
    var yyyyMMdd = String(date_str);
    var days = "31";
    var year = yyyyMMdd.substring(0,4);
    var month = yyyyMMdd.substring(4,6);

    if (Number(month) == 2)
    {
        if (is_leap_year(year+month+"01"))
            days = "29";
        else
            days = "28";
    }
else if (Number(month) == 4 || Number(month) == 6 || Number(month) == 9 || Number(month) == 11)
        days = "30";

    return days;
}

오늘 날짜 중 연도 반환

function get_today_year()
{
    var today = new Date();
    return today.getYear();
}

오늘 날짜 중 Month반환. format: MM

function get_today_month()
{
    var today = new Date();
    return (today.getMonth()+1) > 9 ?  (today.getMonth()+1) : "0" + (today.getMonth()+1)
}

참고

https://java119.tistory.com/76

 

[JavaScript] 날짜 관련 유용 함수 총 정리(feat.String to Date)

입력한 날짜(yyyyMMdd)가 유효한 날짜인지 검사 function is_valid_date(date_str) { var yyyyMMdd = String(date_str); var year = yyyyMMdd.substring(0,4); var month = yyyyMMdd.substring(4,6); var day = yy..

java119.tistory.com

html

<table id="factory_table" class="table" style="margin-top: 20px;">
	<colgroup>
		<col width="20%"/>
		<col width="10%"/>
		<col width="30%"/>
		<col width="30%"/>
		<col width="10%"/>
	</colgroup>
	
	<thead>
		<tr>
			<th>이름(설비)</th>
			<th>대수</th>
			<th>기계 및 컨트롤러 정보</th>
			<th>공정명</th>
			<th></th>
		</tr>
	</thead>

	<tbody id="factory_tbody">
		<tr>
			<td> <input type="text" class="form-control" placeholder="설비명">  </td>
			<td> <input type="number" class="form-control" placeholder="설치 대수" onkeypress="return event.charCode >= 48 && event.charCode <= 57">  </td>
			<td> <input type="text" class="form-control" placeholder="기계 및 컨트롤러 정보">  </td>
			<td> <input type="text" class="form-control" placeholder="공정명">  </td>
			<td></td>
		</tr>
	</tbody>
</table>

Table 행 추가

// 하나의 Row 입력
var rowItem = "<tr>"
rowItem += "<td> <input type='text' class='form-control' placeholder='설비명'> </td>"
rowItem += "<td> <input type='number' class='form-control' placeholder='설치 대수' onkeypress='return event.charCode >= 48 && event.charCode <= 57'> </td>"
rowItem += "<td> <input type='email' class='form-control' placeholder='기계 및 컨트롤러 정보'> </td>"
rowItem += "<td> <input type='text' class='form-control' placeholder='공정명'> </td>"
rowItem += "<td> <button type='button' class='btn btn-danger'> <i class='fa fa-minus'></i> </button> </td>"
rowItem += "</tr>"
$('#factory_table').append(rowItem)




// Table 행을 모두 삭제 후 리스트 형식 입력 ( 예제와 별도 형식 )
var TableRowsList = ["'a','b','c','d','e'","'1','2','3','4','5'",,,,]  <-- Table rows list가 들어 있다는 가정..

$('#factory_table').remove();

var rowItem = '<tr>'
rowItem += '<th>이름(설비)</th>'
rowItem += '<th>대수</th>'
rowItem += '<th>기계 및 컨트롤러 정보</th>'
rowItem += '<th>공정명</th>'
rowItem += '<th></th>'
rowItem += '</tr>'
$('#factory_table').append(rowItem)
            
for(key in TableRowsList ){
    RowList = TableRowsList[key].split(',')
    var rowItem = '<tr>'
    rowItem += '<td> ' + RowList[1] + ' </td>'
    rowItem += '<td> ' + RowList[2] + ' </td>'
    rowItem += '<td> ' + RowList[3] + ' </td>'
    rowItem += '<td> ' + RowList[4] + ' </td>'
    rowItem += '<td> ' + RowList[5] + ' </td>'
    rowItem += '</tr>'
    $('#factory_table').append(rowItem)
}

Table 행 삭제

$('#factory_table').on("click", "button", function() {
    $(this).closest("tr").remove()
});

입력된 Table 행 데이터 가져오기

$('#factory_tbody tr').each(function () {
	var cellItem = $(this).find(":input")
	var itemObj = new Object()
	itemObj.title = cellItem.eq(0).val()
	itemObj.count = cellItem.eq(1).val()
	itemObj.info = cellItem.eq(2).val()
	itemObj.name = cellItem.eq(3).val()
})

참고

https://elfinlas.github.io/2017/12/25/devnote01/

 

12월 18일 개발일지 (HTML Table의 행을 동적으로 처리하기 with jQuery)

HTML에서 Table 요소 처리이번에 제가 만드는 사내정보시스템을 이번에 저희 회사에도 도입을 하게 되면서, 회사에 특화된 기능을 개발하게 되었습니다.그런데 이번에 Html의 테이블 요소를 다루게

elfinlas.github.io

 

[PHP] php test page

http://phptester.net/

 

PHPTESTER - Test PHP code online

This application is free so please don't break it! Contact :phptester.net@gmail.com Dear Hacker you don't need to hack this free website to prove that you are very good!!!! Updates: I'm trying a new approche, PHP 7.1 to 7.4 version added but still in test

phptester.net

 

문자열에 특정 문자가 포함되어 있는지 확인

# Check 함수
function strpos_array($haystack, $needle) {
    $pos = FALSE;
    if (!is_array($needle)) $needle = array($needle);
    foreach ($needle as $what) {
        if (($pos = strpos($haystack, $what)) !== FALSE) {
            return $pos;
        }
    }
    return FALSE;
}

$filters = array("TEST", "test", "Pass", "PASS");
$strText = "The TEST Code !!"
if (strpos_array($strText, $filters) !== false) {
    // nothing to do
    echo json_encode("Text has been filtered");
    return;
}

to Top