Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Clark Kents TypeScript Adventures - Educating ES2015 to MVPs (Microsoft Pros)

NotificationsYou must be signed in to change notification settings

Danny-Engelman/CKTA

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

91 Commits
 
 

Repository files navigation

These code examples come fromSPFx Examples submitted by MVPs

If you see your own code and feel ashamed. Good! You were writing oldskool ES3 code!

Are you Superman?

You mightfeel like Superman with TypeScript, but if you take off your cape,you are coding JavaScript ES6
And if you don't master ES6 you will still be writing oldskool ES3, long and error prone code
These are live examples where Clark Kent refactored Superman code

All code fragments are for educational purposes, to explain ES6 concepts, and are not necessarily production ready code

Links to Original code can be broken


Forget For!

A For loop and break to find something in an Array should be something of the past

privategetValueFromResults(key:string,results:ISearchResultValue[]): string{letvalue:string='';if(results!=null&&results.length>0&&key!=null){for(leti:number=0;i<results.length;i++){constresultItem:ISearchResultValue=results[i];if(resultItem.Key===key){value=resultItem.Value;break;}}}returnvalue;}

Clark Kents' code:

privategetValueFromResults(key:string,results:ISearchResultValue[]): string{letitem=results.find(result=>result.Key===key);returnitem ?item.Value :'empty string';}

.map() and .reduce() methods are powerful!

And easily replace the too often used (or only one learned) .forEach method

.then((response:{PrimaryQueryResult:IPeopleDataResult}):void=>{letrelevantResults:any=response.PrimaryQueryResult.RelevantResults;letresultCount:number=relevantResults.TotalRows;letpeople=[];letpersona:IPersonaProps={};if(resultCount>0){relevantResults.Table.Rows.forEach(function(row){row.Cells.forEach(function(cell){//person[cell.Key] = cell.Value;if(cell.Key==='JobTitle')persona.secondaryText=cell.Value;if(cell.Key==='PictureURL')persona.imageUrl=cell.Value;if(cell.Key==='PreferredName')persona.primaryText=cell.Value;});people.push(persona);});}resolve(people);},(error:any):void=>{reject(this._peopleList=[]);}));

Clark Kents' code:

.then((response:{PrimaryQueryResult:IPeopleDataResult}):void=>{resolve(response.PrimaryQueryResult.RelevantResults.Table.Rows.map(row=>row.Cells.reduce((persona,cell)=>{if(cell.Key==='JobTitle')persona.secondaryText=cell.Value;if(cell.Key==='PictureURL')persona.imageUrl=cell.Value;if(cell.Key==='PreferredName')persona.primaryText=cell.Value;returnpersona;},{})// start with empty {} persona object));},(error:any):void=>reject(this._peopleList=[]));

Let your codeflow logical

Resolve and Returns statements, it is hard to see the flow of this functions

returnnewPromise((resolve,reject)=>{varwriteManifestsTask=undefined;for(vari=0;i<config.uniqueTasks.length;i++){if(config.uniqueTasks[i].name==='writemanifests'){writeManifestsTask=config.uniqueTasks[i];break;}}if(!writeManifestsTask){varerrorMsg='Couldn\'t retrieve the writeManifests task.';error(errorMsg);reject(errorMsg);return;}varurl=config.production ?`${writeManifestsTask.taskConfig.cdnBasePath}` :`${writeManifestsTask.taskConfig.debugBasePath}dist/`;varwebPartCodePath=`${config.libFolder}/webparts/angularMsGraph/AngularMsGraphWebPart.js`;varwebPartCode=fs.readFileSync(webPartCodePath,'utf-8');webPartCode=webPartCode.replace('$BASEURL$',url);fs.writeFile(webPartCodePath,webPartCode,(err)=>{if(err){error(err);reject(err);return;}log(`Base URL successfully set to${url}`);resolve();});});}

Clark Kents' code:

returnnewPromise((resolve,reject)=>{functiontaskerror(err){error(err);reject(err);}config.uniqueTasks.filter(task=>task.name==='writemanifests').forEach(task=>{leturl=config.production ?task.taskConfig.cdnBasePath :task.taskConfig.debugBasePath+"dist/";letwebPartCodePath=config.libFolder+"/webparts/angularMsGraph/AngularMsGraphWebPart.js";letwebPartCode=fs.readFileSync(webPartCodePath,'utf-8').replace('$BASEURL$',url);fs.writeFile(webPartCodePath,webPartCode,(err)=>{if(err)taskerror(err);log(`Base URL successfully set to${url}`);resolve();});});taskerror("Couldn't retrieve the writeManifests task.");}

Again, For loops can be replaced with (many) Array functions

Objective: Fade in (set opacity) N of M DOM elements

see source for all code, with some more refactoringit can be reduced to half the size

private_initializeOpacities():void{    leti=0;letj=1;letopacity;this.fadeIncrement=1/this.numCircles;for(i;i<this.numCircles;i++){letcircleObject=this.circleObjects[i];opacity=(this.fadeIncrement*j++);this._setOpacity(circleObject.element,opacity);}}private_setOpacity(element:HTMLElement,opacity: number):void{    element.style.opacity=opacity.toString();}

Clark Kents'code:

private_initializeOpacities():void{[...this.circleObjects].some((circle,idx)=>{circle.style.opacity=++idx/this.numCircles;returnidx===this.numCircles;//end .some() method when true});}

some() executes the callback function once for each element present in the array until it finds one where callback returns a truthy value (a value that becomes true when converted to a Boolean).If such an element is found, some() immediately returns true. Otherwise, some() returns false.

Same but shorter, without the return statement:

private_initializeOpacities():void{[...this.circleObjects].some((circle,idx)=>(circle.style.opacity=++idx/this.numCircles,idx===this.numCircles));}

Make you code read like it flows!

Traditionaly we had to declare strings etc. up front before we could use them.
The one drawback is that your code no longer reads the same as it flows
With some careful refactoring you can not only save on used variables (and thus performance) but also shorten your code

privaterenderData():void{if(this.orders===null){return;//CK: Why does a void function return an undefined value?}letordersString:string="";//CK:String concation is oldskool since Microsoft (finally) implemented the .map /.reduce methods in IE9this.orders.forEach(order=>{ordersString+=`<tr>  <tdpl-s1">${styles.number}">${order.id}</td>  <tdpl-s1">${styles.number}">${newDate(order.orderDate).toLocaleDateString()}</td>  <td>${order.region.toString()}</td>  <td>${order.rep}</td>  <td>${order.item}</td>  <tdpl-s1">${styles.number}">${order.units}</td>  <tdpl-s1">${styles.number}">$${order.unitCost.toFixed(2)}</td>  <tdpl-s1">${styles.number}">$${order.total.toFixed(2)}</td></tr>`;});consttable:Element=this.domElement.querySelector(".data");table.removeAttribute("style");table.querySelector("tbody").innerHTML=ordersString;}

Clark Kents' code:

privaterenderData():void{if(this.orders){constclassName=styles.number;// GLOBAL!consttable:Element=this.domElement.querySelector(".data");table.removeAttribute("style");//could use standard HTML5 'hidden' attribute instead of whole Styletable.querySelector("tbody").innerHTML=this.orders.map(order=>`<tr>                    <tdpl-s1">${className}">${order.id}</td>                    <tdpl-s1">${className}">${newDate(order.orderDate).toLocaleDateString()}</td>                    <td>${order.region.toString()}</td>                    <td>${order.rep}</td>                    <td>${order.item}</td>                    <tdpl-s1">${className}">${order.units}</td>                    <tdpl-s1">${className}">$${order.unitCost.toFixed(2)}</td>                    <tdpl-s1">${className}">$${order.total.toFixed(2)}</td>                  </tr>`).join('');}}

Make you code read like it flows! - The Sequel

publiccomponentWillReceiveProps(newProps:IPanelProps){if(newProps.isOpen===this.props.isOpen)return;clearTimeout(this._onCloseTimer);if(newProps.isOpen){if(!this.state.isOpen){this.setState({isOpen:true});}else{this.setState({isVisible:true});}}if(!newProps.isOpen&&this.state.isOpen){this._close();}}

Function returnsundefined if it is not the currect property-pane, alter the code to only execute the body

Negations!this.state.isOpen are hard to read, since there IS an else branch, the then/else can be reversed

The first condition of the last (3rd) IF is actually the else branch, so no need to check something twice

Refactored code

publiccomponentWillReceiveProps(newProps:IPanelProps){if(newProps.isOpen!==this.props.isOpen){clearTimeout(this._onCloseTimer);if(newProps.isOpen){if(this.state.isOpen)this.setState({isVisible:true});elsethis.setState({isOpen:true});}elseif(this.state.isOpen){this._close();}}}

Condense that code! It will minify better! And is maintainable

Note: In the above example you can replace 'this' with a constant to make it minifiy better!

privatesetButtonsEventHandlers():void{constwebPart:JsomCrudWithBatchWebPart=this;this.domElement.querySelector('button.create-Button').addEventListener('click',()=>{webPart.createItem();});this.domElement.querySelector('button.readall-Button').addEventListener('click',()=>{webPart.readItems();});this.domElement.querySelector('button.update-Button').addEventListener('click',()=>{webPart.updateItem();});this.domElement.querySelector('button.delete-Button').addEventListener('click',()=>{webPart.deleteItem();});}

Clark Kents' code:

privatesetButtonsEventHandlers():void{constwebPart:JsomCrudWithBatchWebPart=this;letaddClick=(crud,func)=>webPart.domElement.querySelector(`button.${crud}-Button`).addEventListener('click',()=>func);addClick('create',webPart.createItem);addClick('readall',webPart.readItems);addClick('update',webPart.updateItem);addClick('delete',webPart.deleteItem);}

By now you should understand the For loop is eval

privateupdateItemsHtml(items:IListItem[]):void{constitemsHtml:string[]=[];for(leti:number=0;i<items.length;i){itemsHtml.push(`<li>${items[i].Title} (${items[i].Id})</li>`);}this.domElement.querySelector('.items').innerHTML=itemsHtml.join('');}

Clark Kents' code:

privateupdateItemsHtml(items:IListItem[]):void{this.domElement.querySelector('.items').innerHTML=items.map(item=>`<li>${items.Title} (${items.Id})</li>`).join('');}

Learn .map

varstringOfId='';data.forEach(element=>{stringOfId+=element.Id+',';// this will add a trailing ,});beforeCallback.updateStatus(`Item with IDs:${stringOfId} successfully updated`);

Clark Kents' code:

varstringOfId=data.map(element=>element.Id).join(',');beforeCallback.updateStatus(`Item with IDs:${stringOfId} successfully updated`);

When you hit Ctrl-C/V alarmbells should go off

Classic example where TypeScript strong Typing forces you to write duplicate code

Abstract endpoint calling into one method..
just think how many references you need to check when the HttpClient is upgraded to V2

publicgetLists():Promise<IList[]>{varhttpClientOptions :ISPHttpClientOptions={};httpClientOptions.headers={'Accept':'application/json;odata=nometadata','odata-version':''};returnnewPromise<IList[]>((resolve:(results:IList[])=>void,reject:(error:any)=>void):void=>{this.context.spHttpClient.get(this.context.pageContext.web.serverRelativeUrl+`/_api/web/lists?$select=id,title`,SPHttpClient.configurations.v1,httpClientOptions).then((response:SPHttpClientResponse):Promise<{value:IList[]}>=>{returnresponse.json();}).then((lists:{value:IList[]}):void=>{resolve(lists.value);},(error:any):void=>{reject(error);});});}publicgetList(listName: string):Promise<IListItem[]>{varhttpClientOptions :ISPHttpClientOptions={};httpClientOptions.headers={'Accept':'application/json;odata=nometadata','odata-version':''};returnnewPromise<IListItem[]>((resolve:(results:IListItem[])=>void,reject:(error:any)=>void):void=>{this.context.spHttpClient.get(this.context.pageContext.web.serverRelativeUrl+`/_api/web/lists('${listName}')/items?$select=Id,Title`,SPHttpClient.configurations.v1,httpClientOptions).then((response:SPHttpClientResponse):Promise<{value:IListItem[]}>=>{returnresponse.json();}).then((listItems:{value:IListItem[]}):void=>{resolve(listItems.value);},(error:any):void=>{reject(error);});});}

Clark Kents' code:

publicgetEndpoint(endpoint:string):Promise<IList[]>{varhttpClientOptions :ISPHttpClientOptions={};httpClientOptions.headers={'Accept':'application/json;odata=nometadata','odata-version':''};constcontext=this.context;returnnewPromise((resolve:(results:any)=>void,reject:(error:any)=>void):void=>{context.spHttpClient.get(context.pageContext.web.serverRelativeUrl+endpoint,SPHttpClient.configurations.v1,httpClientOptions).then(response=>response.json()).then(listdata=>resolve(listdata.value)).catch(error=>reject(error));})}publicgetLists():Promise<IList[]>{returngetEndpoint('/_api/web/lists?$select=id,title');}publicgetList(listName: string):Promise<IListItem[]>{returngetEndpoint(`/_api/web/lists('${listName}')/items?$select=Id,Title`);}

About

Clark Kents TypeScript Adventures - Educating ES2015 to MVPs (Microsoft Pros)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp