You are on page 1of 7

ComponentStatesinAngular2

WhatifcomponentsinAngular2werewiredalwayshaveaninternalstatewhichallowedfor
otherpartsoftheframeworktodopreparatoryUIrelatedworksothatwhenthatstatechanges
Angularcanattempttotakeonasmuchoftheanimationaspossible.

Settingthefoundationforanimations
AnimationsinAngular2.0arenothereyet,however,statechangesbetweenacomponentor
multiplestatechangesamongmultiplecomponentswouldbeaniceplatformforAnimationsto
betriggeredon.

BasicStateChange
Letsimaginethatwehaveacomponentcalled<zippy>whichisdesignedtoopenandclose.
Bydefinitiontherearetwostatesandthesetwostatesarestatesthatthecomponentmanages
itselfwithinitsdefinition:

@Component({
selector:zippy
})
classZippy{
open:Boolean
}

Triggeringanimations,however,isnonexistentsinceAngulardoesntknowthattheopen/close
booleanvaluedictatesthestateofthecomponent.Thereforewewillplaceavaluewithinthe
annotationtoreflectthat:

@Component({
selector:zippy
state:open//the`open`booleanmembervalueiswatched
})

Nowwhentheopenstatechangesitwillimposethatchangeontoitscomponentcontainer.
Nowitswhenthestatechangeoccursthattheanimationwilltakeoff.Letsimaginethatour
CSStransitioncodebelowwillmatchthestatechangeforthezippycomponent:

/*rememberthatyoucanalsoplaceaclassontheelementand
targetthatonespecifically*/
zippy.fromclosed.toopen{
transition:0.5slinearall
}


NoticehowthereisnoactualCSSstylingcodeinheretotellthetransitionwhattodo?Allthat
wehavehereisatransitionvaluewhichwillthenactivatethestatetransitionforus.Nowwhat
abouttheactualstylechangesthatoccurforthezippyforitsopenandclosedstates?Since
thosestateshavenothingtodowithanimation,andarearequiredpiecetoexistforwhenthe
zippyisopenorclosed,theCSSstylesforthezippyaredefinedwithinthecomponent
container.

zippy.ngopen{
maxheight:500px
}

zippy.ngclosed{
maxheight:500px
}

Sinceweprovidedatransitionforthestatechangethenthatsallrequiredforthecomponentto
doitsthing.

ImplicitStatesEnter/Leave/Move
WhenacomponentisinsertedintotheDOMthenthatisastatechangefromdetachedto
whateverstatethecomponentwillbeoradefaultstatecalledngdefault.Whena
componentisremovedthenitwillbemovedfromitscurrentstatetodetached.

SoifwesetourzippytobeopenbydefaultwhenitisplacedintheDOMthenthestateshould
gofromdetachedtoopen.

<zippy*ngif=yes[open]=yes></zippy>

zippy.ngdetached{
opacity:0
}

zippy.ngopen{
maxheight:500px
}

zippy.fromdetached.toopen{
transition:0.5slinearall
}

Nowwhentheitemisinsertedthenitwillperformthisanimationautomatically.

GroupbasedStateChangeSequencing
Statechangesbecomepowerfulwhenthereareamultitudeofstatechangesthatoccur
betweenacollectionofelements.LetssayforexamplewehadthefollowingHTMLcodeforour
viewonourpage:

<!home.html

<h2>HomePage</h2>
<div*ngif=showUsersclass=userscontainer>
<div*ngfor=#userofusersclass=user>
{{user.name}}
</div>
</div>

NowwhathappensifshowUserschangestotrue?Theoutercontainerisevaluatedand
insertedintothepageandtheinnercontainerisalsoevaluatedanditslistofusersisalso
insertedintothepage.Ifnoanimationsaredetectedthennothinghappens,however,if
animationsaredetectedonboththe.userscontainerand.userelementcontainersthenthe
parentcontainer(inthiscase.userscontainer)willwinandtheinnerelementswillnotanimate.
HoweverifweusesomespecialCSScodethenwecanperformananimationsequence
statechangeorchestration.

Togetstartedwiththisletsreiteratethepresenceofthestatechangesforbothsetsof
elements.Theoutercontainerisbeinginsertedandtheinnercontainersaswell.Sowecan
startbydefiningananimationontheoutercontainer:

.userscontainer.fromdetached{
opacity:0
}

.userscontainer.fromdetached.todefault{
transition:0.5slinearall/*thiselementwillfadein*/
}

Nowtheinner.userelementsarealsobeinginsertedfromdetachedtoany,buttheparent
containermaynotknowtheexactdetailsabouttheinnerstatechanges.Soinorderforit
topickupanystatechangeamongitschildrenthenwecouldissueaCSSselectortopickthat
up.

<!thiswillgofromdetachedtoitsdefaultstate
<!butanystatechangewillalsohaveaCSSclassof
ngstatechange>

<div*ngfor=#userofusersclass=user>
{{user.name}}
</div>

Wheneveranycomponentperformsastatechangeatemporaryclassofngstatechange
willbeappliedtotheelementsthatarechangingstate.ThisallowsforaCSSselectortoquery
theinner,changingelementsandissuesomekindofdelayorstaggerorchestration.

ButhowdowedothisinCSS?

CSSCustomStyles/Properties
CustomCSSStyles/PropertiesarepossiblewithahackwhichmakesuseoftheCSScontent
propertybypreparingalistofkey/valueproperties,creatingaJSONobject,andpastingthe
contentsofthatJSONintothatcontentpropertyontheelement.

body{
content:{custom:value}
}

ThisworksinallbrowsersotherthanIE,butifweduplicatethecontenttagandprependitwitha
hyphenthenitworksinIEwhenaccessedviatheelement.currentStylehashobject.

body{
content:{custom:value}
content:{custom:value}
}

TomakethislessuglywewilluseabuildstepinourAngular2.0buildpipelinethatwillmake
custompropertiesconvertthemselvestothis.

Sothis

body{
custom:value
}

Becomesthis:

body{
custom:value
content:{custom:value}
content:{custom:value}

}
ThenngAnimatecanreadthesecustomvaluesdirectlyviagetComputedStyleand
element.currentStyle.

CustomPropertiestoOrchestrateAnimations
SincewecanusecustompropertieswecanputtogetherauniquepropertythattellsngAnimate
toholdofffromanimationsthatoccuronchildelements.

Wecanhaveourparentcontainercalled.userscontainertellthechildanimationstowait:

.userscontainer.fromdetached.todefault{
/*runallchildanimationsfor0.5sandstaggerby0.1s*/
nganimatechildren:*.ngstatechange0.5s0.1s
}

Orissueaspecifictransitionorkeyframetorun

.userscontainer.ngenter{
nganimatechildren:
*.ngstatechange(.someclass0.5s)0.1s

nganimatechildren:
*.ngstatechange(@keyframe0.5s)0.1s

nganimatechildren:
*.ngstatechange(@keyframe0.5s,.someclass0.5s)
0.1s
}

Thenicethinghereisthatthechildanimationsstillanimatetheirownway,buttheparent
container/componentcanoverrideandreorganizetheirbehaviour.

TheJSdriverequivalentcandothesamething.

classPageAnimator{
@Animate({
target:.userscontainer,
children:*.ngstatechange
})
usersAnimation(target,children){
//dowhateveryouwantonthechildelements
varpromises

children.forEach(function(child){
promises.push(newPromise(resolve){
setTimeout(function(){
child.animate(resolve)
},i*100)
}))
})
returnPromise.all(promises)
}
}

AnimatingNonAngularElements
SincebothCSSandJSsequencinguseCSSstyleselectorsthismeansthatwecansequence
animationsonelementsthatarenotassociatedwithAngularorngAnimate.

Thereforesomethinglikethiscouldbeusedtoanimatetheparentelementandthechild
animationswithasingleselector.

.ifcontainer.fromdetached.todefault{
nganimate:*(.fade0.5s)serial
}

<div*ngif=exp>
<p>...</p>
<p>...</p>
<div[css.hide]=exp>...</div>
<p>...</p>
<p>...</p>
</div>

Everythinginherenowanimatesonebyone.

IntegrationwiththeRouter
Sinceeachcontainercontainsastate,therouterwillautomaticallyissuestatesbasedonthe
aliasvaluedefinedineachroute.

@RouteConfig({
/:{as:home,component:HomeCmp},
/about:{as:about,component:AboutCmp},
})

Nowwhentheroutechanges,thecomponentcontainerwillautomaticallyincludethestate
change:

appcomponent.fromhomeroute.toaboutroute{
nganimate:*.todetached(.fadeout0.5s)serial,
*.fromdetached(.fadein0.5s)serial
}

CustomStatesfromthetemplate
Sincestatechangesareissuedfromwithinthecomponent,itgetsdifficulttoapplyyourown
statevaluesfromwithinthetemplatewithoutcreatingacomponentofyourown.Sayfor
examplewehad*ngifandwewantedtohaveourownstateworkwithit:

<!theonlystateshereareenter/leave>
<div*ngif=exp></div>

Butifweusethengstateattributethenitwillwork:

<!theonlystateshereareenter/leave>
<div*ngif=exp[ngstate]=myCustomState></div>

You might also like