1313import dash_html_components as html
1414import dash_core_components as dcc
1515
16- from dash import Dash , callback_context
16+ from dash import Dash , callback_context , no_update
1717
1818from dash .dependencies import Input , Output , State
1919from dash .exceptions import (
@@ -150,7 +150,10 @@ def update_text(data):
150150 assert_clean_console (self )
151151
152152 def test_aborted_callback (self ):
153- """Raising PreventUpdate prevents update and triggering dependencies"""
153+ """
154+ Raising PreventUpdate OR returning no_update
155+ prevents update and triggering dependencies
156+ """
154157
155158 initial_input = 'initial input'
156159 initial_output = 'initial output'
@@ -168,6 +171,8 @@ def test_aborted_callback(self):
168171 @app .callback (Output ('output1' , 'children' ), [Input ('input' , 'value' )])
169172 def callback1 (value ):
170173 callback1_count .value += 1
174+ if callback1_count .value > 2 :
175+ return no_update
171176 raise PreventUpdate ("testing callback does not update" )
172177 return value
173178
@@ -180,12 +185,12 @@ def callback2(value):
180185
181186 input_ = self .wait_for_element_by_id ('input' )
182187 input_ .clear ()
183- input_ .send_keys ('x ' )
188+ input_ .send_keys ('xyz ' )
184189 output1 = self .wait_for_element_by_id ('output1' )
185190 output2 = self .wait_for_element_by_id ('output2' )
186191
187- # callback1 runs twice (initial page load and through send_keys)
188- self .assertEqual (callback1_count .value , 2 )
192+ # callback1 runs 4x (initial page load and 3x through send_keys)
193+ self .assertEqual (callback1_count .value , 4 )
189194
190195 # callback2 is never triggered, even on initial load
191196 self .assertEqual (callback2_count .value , 0 )
@@ -656,6 +661,39 @@ def overlapping_multi_output(n_clicks):
656661
657662 self .assertGreater (int (output2 .text ), t )
658663
664+ def test_multi_output_no_update (self ):
665+ app = Dash (__name__ )
666+ app .scripts .config .serve_locally = True
667+
668+ app .layout = html .Div ([
669+ html .Button ('B' , 'btn' ),
670+ html .P ('initial1' , 'n1' ),
671+ html .P ('initial2' , 'n2' ),
672+ html .P ('initial3' , 'n3' )
673+ ])
674+
675+ @app .callback ([Output ('n1' , 'children' ),
676+ Output ('n2' , 'children' ),
677+ Output ('n3' , 'children' )],
678+ [Input ('btn' , 'n_clicks' )])
679+ def show_clicks (n ):
680+ # partial or complete cancelation of updates via no_update
681+ return [
682+ no_update if n > 4 else n ,
683+ no_update if n > 2 else n ,
684+ no_update
685+ ]
686+
687+ self .startServer (app )
688+
689+ btn = self .wait_for_element_by_id ('btn' )
690+ for _ in range (10 ):
691+ btn .click ()
692+
693+ self .wait_for_text_to_equal ('#n1' , '4' )
694+ self .wait_for_text_to_equal ('#n2' , '2' )
695+ self .wait_for_text_to_equal ('#n3' , 'initial3' )
696+
659697 def test_with_custom_renderer (self ):
660698 app = Dash (__name__ )
661699
0 commit comments