780 lines
61 KiB
HTML
780 lines
61 KiB
HTML
<!DOCTYPE HTML>
|
||
<html lang="en" class="coal" dir="ltr">
|
||
<head>
|
||
<!-- Book generated using mdBook -->
|
||
<meta charset="UTF-8">
|
||
<title>creating custom directives - Andrew's Blog</title>
|
||
|
||
|
||
<!-- Custom HTML head -->
|
||
|
||
<meta name="description" content="Andrew Ryan's Blog">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta name="theme-color" content="#ffffff">
|
||
|
||
<link rel="icon" href="../../favicon.svg">
|
||
<link rel="shortcut icon" href="../../favicon.png">
|
||
<link rel="stylesheet" href="../../css/variables.css">
|
||
<link rel="stylesheet" href="../../css/general.css">
|
||
<link rel="stylesheet" href="../../css/chrome.css">
|
||
|
||
<!-- Fonts -->
|
||
<link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
|
||
<link rel="stylesheet" href="../../fonts/fonts.css">
|
||
|
||
<!-- Highlight.js Stylesheets -->
|
||
<link rel="stylesheet" href="../../highlight.css">
|
||
<link rel="stylesheet" href="../../tomorrow-night.css">
|
||
<link rel="stylesheet" href="../../ayu-highlight.css">
|
||
|
||
<!-- Custom theme stylesheets -->
|
||
<link rel="stylesheet" href="../../src/style/custom.css">
|
||
|
||
<!-- MathJax -->
|
||
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||
</head>
|
||
<body class="sidebar-visible no-js">
|
||
<div id="body-container">
|
||
<!-- Provide site root to javascript -->
|
||
<script>
|
||
var path_to_root = "../../";
|
||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "coal" : "coal";
|
||
</script>
|
||
|
||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||
<script>
|
||
try {
|
||
var theme = localStorage.getItem('mdbook-theme');
|
||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||
|
||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||
}
|
||
|
||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||
}
|
||
} catch (e) { }
|
||
</script>
|
||
|
||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||
<script>
|
||
var theme;
|
||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||
var html = document.querySelector('html');
|
||
html.classList.remove('coal')
|
||
html.classList.add(theme);
|
||
var body = document.querySelector('body');
|
||
body.classList.remove('no-js')
|
||
body.classList.add('js');
|
||
</script>
|
||
|
||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||
|
||
<!-- Hide / unhide sidebar before it is displayed -->
|
||
<script>
|
||
var body = document.querySelector('body');
|
||
var sidebar = null;
|
||
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||
if (document.body.clientWidth >= 1080) {
|
||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||
sidebar = sidebar || 'visible';
|
||
} else {
|
||
sidebar = 'hidden';
|
||
}
|
||
sidebar_toggle.checked = sidebar === 'visible';
|
||
body.classList.remove('sidebar-visible');
|
||
body.classList.add("sidebar-" + sidebar);
|
||
</script>
|
||
|
||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||
<div class="sidebar-scrollbox">
|
||
<ol class="chapter"><li class="chapter-item affix "><a href="../../index.html">Andrew's Blog</a></li><li class="chapter-item "><a href="../../posts/linux/linux.html"><strong aria-hidden="true">1.</strong> linux</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/linux/install_linux.html"><strong aria-hidden="true">1.1.</strong> install linux</a></li><li class="chapter-item "><a href="../../posts/linux/bash_profile.html"><strong aria-hidden="true">1.2.</strong> bash profile</a></li><li class="chapter-item "><a href="../../posts/linux/command_list.html"><strong aria-hidden="true">1.3.</strong> command list</a></li><li class="chapter-item "><a href="../../posts/linux/git_guide.html"><strong aria-hidden="true">1.4.</strong> git guide</a></li><li class="chapter-item "><a href="../../posts/linux/tar.html"><strong aria-hidden="true">1.5.</strong> tar</a></li><li class="chapter-item "><a href="../../posts/linux/run_x86_elf_in_x64_setup.html"><strong aria-hidden="true">1.6.</strong> run x86 elf in x64 setup</a></li></ol></li><li class="chapter-item "><a href="../../posts/mac/mac.html"><strong aria-hidden="true">2.</strong> mac</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/mac/macos_profiles.html"><strong aria-hidden="true">2.1.</strong> macos profiles</a></li></ol></li><li class="chapter-item "><a href="../../posts/swift/swift.html"><strong aria-hidden="true">3.</strong> swift</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/swift/learn_swift.html"><strong aria-hidden="true">3.1.</strong> learn swift basics</a></li><li class="chapter-item "><a href="../../posts/swift/swift_extensions.html"><strong aria-hidden="true">3.2.</strong> Swift extensions</a></li><li class="chapter-item "><a href="../../posts/swift/swiftui_extension.html"><strong aria-hidden="true">3.3.</strong> SwiftUI extensions</a></li><li class="chapter-item "><a href="../../posts/swift/install_swift.html"><strong aria-hidden="true">3.4.</strong> install swift</a></li><li class="chapter-item "><a href="../../posts/swift/task_planner.html"><strong aria-hidden="true">3.5.</strong> implment task panner app with SwiftUI</a></li><li class="chapter-item "><a href="../../posts/swift/swift_cheat_sheet.html"><strong aria-hidden="true">3.6.</strong> Swift Cheat Sheet</a></li><li class="chapter-item "><a href="../../posts/swift/yinci_url.html"><strong aria-hidden="true">3.7.</strong> Personal privacy protocol</a></li><li class="chapter-item "><a href="../../posts/swift/swift_regular_exressions.html"><strong aria-hidden="true">3.8.</strong> Swift regular exressions</a></li><li class="chapter-item "><a href="../../posts/ios/how_to_create_beautiful_ios_charts_in_swift.html"><strong aria-hidden="true">3.9.</strong> How to Create Beautiful iOS Charts in鑱絊wift</a></li><li class="chapter-item "><a href="../../posts/swift/swiftui_source_code.html"><strong aria-hidden="true">3.10.</strong> SwiftUI source code</a></li><li class="chapter-item "><a href="../../posts/swift/use_swift_fetch_iciba_api.html"><strong aria-hidden="true">3.11.</strong> use swift fetch iciba API</a></li></ol></li><li class="chapter-item "><a href="../../posts/ios/ios.html"><strong aria-hidden="true">4.</strong> ios</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/ios/cocaposd_setup_and_install_for_ios_project.html"><strong aria-hidden="true">4.1.</strong> cocaposd setup and install for ios project</a></li><li class="chapter-item "><a href="../../posts/ios/swiftui_show_gif_image.html"><strong aria-hidden="true">4.2.</strong> SwiftUI show gif image</a></li><li class="chapter-item "><a href="../../posts/ios/implement_task_planner_app.html"><strong aria-hidden="true">4.3.</strong> implement Task planner App</a></li></ol></li><li class="chapter-item "><a href="../../posts/objective_c/objective_c.html"><strong aria-hidden="true">5.</strong> objective_c</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/objective_c/objective_c_cheat_sheet.html"><strong aria-hidden="true">5.1.</strong> Objective-C Cheat Sheet</a></li><li class="chapter-item "><a href="../../posts/objective_c/objective_c_for_absolute_beginners_read_note.html"><strong aria-hidden="true">5.2.</strong> Objective-C Note</a></li></ol></li><li class="chapter-item "><a href="../../posts/dart/dart.html"><strong aria-hidden="true">6.</strong> dart</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/dart/flutter.html"><strong aria-hidden="true">6.1.</strong> Flutter Cheat Sheet</a></li><li class="chapter-item "><a href="../../posts/dart/dart_cheat_sheet.html"><strong aria-hidden="true">6.2.</strong> Dart Cheat Sheet</a></li><li class="chapter-item "><a href="../../posts/flutter/flutter_dev_test.html"><strong aria-hidden="true">6.3.</strong> Flutter dev test</a></li></ol></li><li class="chapter-item "><a href="../../posts/rust/rust.html"><strong aria-hidden="true">7.</strong> rust</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/rust/offline_use_rust.html"><strong aria-hidden="true">7.1.</strong> Offline use rust</a></li><li class="chapter-item "><a href="../../posts/rust/rust_grammer.html"><strong aria-hidden="true">7.2.</strong> rust grammar</a></li><li class="chapter-item "><a href="../../posts/rust/pase_string_and_decimal_conversion.html"><strong aria-hidden="true">7.3.</strong> pase string and decimal conversion</a></li><li class="chapter-item "><a href="../../posts/rust/parse_types.html"><strong aria-hidden="true">7.4.</strong> rust types</a></li><li class="chapter-item "><a href="../../posts/rust/rust_life_cycle.html"><strong aria-hidden="true">7.5.</strong> Rust life cycle</a></li><li class="chapter-item "><a href="../../posts/rust/rust_generic.html"><strong aria-hidden="true">7.6.</strong> rust generics</a></li><li class="chapter-item "><a href="../../posts/rust/rust_implment_matrix.html"><strong aria-hidden="true">7.7.</strong> Rust implement matrix</a></li><li class="chapter-item "><a href="../../posts/rust/rust_sort.html"><strong aria-hidden="true">7.8.</strong> Rust implement sort algorithms</a></li><li class="chapter-item "><a href="../../posts/rust/implement_aes_encryption.html"><strong aria-hidden="true">7.9.</strong> Rust implement AEC encryption and decryption</a></li><li class="chapter-item "><a href="../../posts/rust/implement_trie_data_structure.html"><strong aria-hidden="true">7.10.</strong> implement trie data structure</a></li><li class="chapter-item "><a href="../../posts/rust/rust_implement_tree.html"><strong aria-hidden="true">7.11.</strong> implement tree data_structure</a></li><li class="chapter-item "><a href="../../posts/rust/list_dir.html"><strong aria-hidden="true">7.12.</strong> list dir</a></li><li class="chapter-item "><a href="../../posts/rust/fast_way_to_implment_object_trait.html"><strong aria-hidden="true">7.13.</strong> fast way to implment object trait</a></li><li class="chapter-item "><a href="../../posts/rust/compress_rust_binary_size.html"><strong aria-hidden="true">7.14.</strong> compress rust binary size</a></li><li class="chapter-item "><a href="../../posts/rust/implment_file_upload_backend.html"><strong aria-hidden="true">7.15.</strong> impliment file upload</a></li><li class="chapter-item "><a href="../../posts/rust/this_is_add_post_cli_implementation_in_rust.html"><strong aria-hidden="true">7.16.</strong> this is add_post cli implementation in rust</a></li><li class="chapter-item "><a href="../../posts/rust/use_rust_implment_a_copyclipbord_cli.html"><strong aria-hidden="true">7.17.</strong> Use rust implment a copyclipbord CLI</a></li><li class="chapter-item "><a href="../../posts/rust/sqlite_database_add_delete_update_show_in_rust.html"><strong aria-hidden="true">7.18.</strong> sqlite database add delete update show in rust</a></li><li class="chapter-item "><a href="../../posts/rust/implementing_tokio_joinhandle_for_wasm.html"><strong aria-hidden="true">7.19.</strong> Implementing tokio JoinHandle for wasm</a></li><li class="chapter-item "><a href="../../posts/rust/rust_implement_a_crate_for_encode_and_decode_brainfuck_and_ook.html"><strong aria-hidden="true">7.20.</strong> rust implement a crate for encode and decode brainfuck and ook</a></li><li class="chapter-item "><a href="../../posts/rust/slint_builtin_elements.html"><strong aria-hidden="true">7.21.</strong> Slint Builtin Elements</a></li><li class="chapter-item "><a href="../../posts/rust/corporate_network_install_rust_on_windows.html"><strong aria-hidden="true">7.22.</strong> Corporate network install Rust on windows</a></li><li class="chapter-item "><a href="../../posts/rust/rust_binary_file_how_to_judge_static_link_or_dynamic_link_in_macos.html"><strong aria-hidden="true">7.23.</strong> rust binary file how to judge static link or dynamic link in Macos</a></li><li class="chapter-item "><a href="../../posts/rust/rust_binary_include_dir_and_get_contents.html"><strong aria-hidden="true">7.24.</strong> rust binary include dir and get contents</a></li><li class="chapter-item "><a href="../../posts/rust/rust_logger_non-block.html"><strong aria-hidden="true">7.25.</strong> rust logger non-block</a></li><li class="chapter-item "><a href="../../posts/rust/rust_connect_sql_server_database.html"><strong aria-hidden="true">7.26.</strong> rust connect sql server database</a></li><li class="chapter-item "><a href="../../posts/rust/rust_websocket_implment.html"><strong aria-hidden="true">7.27.</strong> rust websocket implment</a></li></ol></li><li class="chapter-item "><a href="../../posts/java/java.html"><strong aria-hidden="true">8.</strong> java</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/java/java_grammar.html"><strong aria-hidden="true">8.1.</strong> java grammar and codewar</a></li><li class="chapter-item "><a href="../../posts/java/run_jar.html"><strong aria-hidden="true">8.2.</strong> java run .jar</a></li><li class="chapter-item "><a href="../../posts/java/java_pomxml_add_defaultgoal_to_build.html"><strong aria-hidden="true">8.3.</strong> Java pomxml add defaultGoal to build</a></li><li class="chapter-item "><a href="../../posts/java/java_set_mvn_mirror.html"><strong aria-hidden="true">8.4.</strong> Java set mvn mirror</a></li></ol></li><li class="chapter-item "><a href="../../posts/python/python.html"><strong aria-hidden="true">9.</strong> python</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/python/convert_pesn.html"><strong aria-hidden="true">9.1.</strong> convert pesn</a></li><li class="chapter-item "><a href="../../posts/python/find_remove_dir.html"><strong aria-hidden="true">9.2.</strong> find and remove dir</a></li><li class="chapter-item "><a href="../../posts/python/timing_message.html"><strong aria-hidden="true">9.3.</strong> wechat send message</a></li><li class="chapter-item "><a href="../../posts/python/use_python_openpyxl_package_read_and_edit_excel_files.html"><strong aria-hidden="true">9.4.</strong> Use python openpyxl package read and edit excel files</a></li></ol></li><li class="chapter-item "><a href="../../posts/go/go.html"><strong aria-hidden="true">10.</strong> go</a></li><li class="chapter-item "><a href="../../posts/js/js.html"><strong aria-hidden="true">11.</strong> js</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/js/js_tutorial.html"><strong aria-hidden="true">11.1.</strong> js tutorial</a></li><li class="chapter-item "><a href="../../posts/js/js_tutorial_map.html"><strong aria-hidden="true">11.2.</strong> ja map</a></li><li class="chapter-item "><a href="../../posts/js/js_tutorial_math.html"><strong aria-hidden="true">11.3.</strong> js math</a></li><li class="chapter-item "><a href="../../posts/js/js_tutorial_object.html"><strong aria-hidden="true">11.4.</strong> js object</a></li><li class="chapter-item "><a href="../../posts/js/js_tutorial_set.html"><strong aria-hidden="true">11.5.</strong> js set</a></li><li class="chapter-item "><a href="../../posts/js/single_thread_and_asynchronous.html"><strong aria-hidden="true">11.6.</strong> single thread and asynchronous</a></li><li class="chapter-item "><a href="../../posts/js/this.html"><strong aria-hidden="true">11.7.</strong> js this</a></li><li class="chapter-item "><a href="../../posts/js/js_implment_aes.html"><strong aria-hidden="true">11.8.</strong> js implment aes</a></li><li class="chapter-item "><a href="../../posts/js/getting_started_with_ajax.html"><strong aria-hidden="true">11.9.</strong> getting started with ajax</a></li><li class="chapter-item "><a href="../../posts/js/BinarySearchTree.html"><strong aria-hidden="true">11.10.</strong> binary search tree</a></li><li class="chapter-item "><a href="../../posts/js/goole_zx.html"><strong aria-hidden="true">11.11.</strong> goole zx</a></li><li class="chapter-item "><a href="../../posts/js/es6.html"><strong aria-hidden="true">11.12.</strong> es6</a></li></ol></li><li class="chapter-item "><a href="../../posts/ruby/ruby.html"><strong aria-hidden="true">12.</strong> ruby</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/ruby/rails_setup_env.html"><strong aria-hidden="true">12.1.</strong> ruby on rails setup environment</a></li><li class="chapter-item "><a href="../../posts/ruby/learn_ruby.html"><strong aria-hidden="true">12.2.</strong> learn ruby</a></li><li class="chapter-item "><a href="../../posts/ruby/ruby_note.html"><strong aria-hidden="true">12.3.</strong> Ruby Note</a></li><li class="chapter-item "><a href="../../posts/ruby/setup_ruby_for_ctf.html"><strong aria-hidden="true">12.4.</strong> Setup ruby for CTF</a></li></ol></li><li class="chapter-item "><a href="../../posts/react/react.html"><strong aria-hidden="true">13.</strong> react</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/react/react_life_cycle.html"><strong aria-hidden="true">13.1.</strong> react life cycle</a></li><li class="chapter-item "><a href="../../posts/react/react_router.html"><strong aria-hidden="true">13.2.</strong> react router</a></li><li class="chapter-item "><a href="../../posts/react/react_this.html"><strong aria-hidden="true">13.3.</strong> react this</a></li><li class="chapter-item "><a href="../../posts/react/react_interviw.html"><strong aria-hidden="true">13.4.</strong> react interview</a></li><li class="chapter-item "><a href="../../posts/react/important_react_interview.html"><strong aria-hidden="true">13.5.</strong> important react interview</a></li><li class="chapter-item "><a href="../../posts/react/react_quick_reference.html"><strong aria-hidden="true">13.6.</strong> react quick reference</a></li><li class="chapter-item "><a href="../../posts/react/redux_quick_reference.html"><strong aria-hidden="true">13.7.</strong> redux quick reference</a></li></ol></li><li class="chapter-item "><a href="../../posts/vue/vue.html"><strong aria-hidden="true">14.</strong> vue</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/vue/vue_ajax.html"><strong aria-hidden="true">14.1.</strong> vue ajax</a></li></ol></li><li class="chapter-item expanded "><a href="../../posts/angular/angular.html"><strong aria-hidden="true">15.</strong> angular</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/angular/controller_communication.html"><strong aria-hidden="true">15.1.</strong> controller communication</a></li><li class="chapter-item expanded "><a href="../../posts/angular/creating_custom_directives.html" class="active"><strong aria-hidden="true">15.2.</strong> creating custom directives</a></li><li class="chapter-item "><a href="../../posts/angular/directive_notes.html"><strong aria-hidden="true">15.3.</strong> directive notes</a></li><li class="chapter-item "><a href="../../posts/angular/directive_communication.html"><strong aria-hidden="true">15.4.</strong> directive communication</a></li><li class="chapter-item "><a href="../../posts/angular/post_params.html"><strong aria-hidden="true">15.5.</strong> post params</a></li><li class="chapter-item "><a href="../../posts/angular/read_json_angular.html"><strong aria-hidden="true">15.6.</strong> read json angular</a></li><li class="chapter-item "><a href="../../posts/angular/same_route_reload.html"><strong aria-hidden="true">15.7.</strong> same route reload</a></li></ol></li><li class="chapter-item "><a href="../../posts/css/css.html"><strong aria-hidden="true">16.</strong> css</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/css/use_css_media.html"><strong aria-hidden="true">16.1.</strong> use css media</a></li></ol></li><li class="chapter-item "><a href="../../posts/php/php.html"><strong aria-hidden="true">17.</strong> php</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/php/for_php_string_implment_some_extemtion_functions.html"><strong aria-hidden="true">17.1.</strong> for php string implment some extemtion functions</a></li><li class="chapter-item "><a href="../../posts/php/php_cheatsheet.html"><strong aria-hidden="true">17.2.</strong> PHP cheatsheet</a></li></ol></li><li class="chapter-item "><a href="../../posts/leetcode/leetcode.html"><strong aria-hidden="true">18.</strong> leetcode</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/leetcode/rust_leetcode.html"><strong aria-hidden="true">18.1.</strong> rust leetcode</a></li><li class="chapter-item "><a href="../../posts/leetcode/rust_codewar.html"><strong aria-hidden="true">18.2.</strong> rust codewar</a></li><li class="chapter-item "><a href="../../posts/leetcode/swift_codewar.html"><strong aria-hidden="true">18.3.</strong> swift codewar</a></li><li class="chapter-item "><a href="../../posts/leetcode/js_leetcode.html"><strong aria-hidden="true">18.4.</strong> js leetcode</a></li><li class="chapter-item "><a href="../../posts/leetcode/java_leetcode.html"><strong aria-hidden="true">18.5.</strong> java leetcode</a></li><li class="chapter-item "><a href="../../posts/leetcode/rust_huawei.html"><strong aria-hidden="true">18.6.</strong> huawei test</a></li><li class="chapter-item "><a href="../../posts/leetcode/rust_utils.html"><strong aria-hidden="true">18.7.</strong> rust common functions</a></li><li class="chapter-item "><a href="../../posts/leetcode/olympiad_training.html"><strong aria-hidden="true">18.8.</strong> Computer olympiad training</a></li></ol></li><li class="chapter-item "><a href="../../posts/ctf/CTF.html"><strong aria-hidden="true">19.</strong> ctf</a><a class="toggle"><div>❱</div></a></li><li><ol class="section"><li class="chapter-item "><a href="../../posts/ctf/CTF_Note.html"><strong aria-hidden="true">19.1.</strong> CTF Note</a></li><li class="chapter-item "><a href="../../posts/ctf/0.1_Web.html"><strong aria-hidden="true">19.2.</strong> Web</a></li><li class="chapter-item "><a href="../../posts/ctf/4.1_Misc.html"><strong aria-hidden="true">19.3.</strong> Misc</a></li><li class="chapter-item "><a href="../../posts/ctf/3.2_PWN_note.html"><strong aria-hidden="true">19.4.</strong> PWN</a></li><li class="chapter-item "><a href="../../posts/ctf/3.1_Crypto.html"><strong aria-hidden="true">19.5.</strong> Crypto</a></li><li class="chapter-item "><a href="../../posts/ctf/3.4_RSA_note.html"><strong aria-hidden="true">19.6.</strong> Rsa attack</a></li><li class="chapter-item "><a href="../../posts/ctf/3.5_Base64.html"><strong aria-hidden="true">19.7.</strong> Base64</a></li><li class="chapter-item "><a href="../../posts/ctf/0.0_SQL Injection Cheatsheet.html"><strong aria-hidden="true">19.8.</strong> SQL Injection Cheatsheet</a></li><li class="chapter-item "><a href="../../posts/ctf/1.1_SQL_injection.html"><strong aria-hidden="true">19.9.</strong> SQL Injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.2_SQL_injection_UNION_attacks.html"><strong aria-hidden="true">19.10.</strong> SQL Injection UNION attacks</a></li><li class="chapter-item "><a href="../../posts/ctf/1.3_Blind SQL injection.html"><strong aria-hidden="true">19.11.</strong> Blind SQL Injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.4_Code Injection.html"><strong aria-hidden="true">19.12.</strong> Code Injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.5_SSRF.html"><strong aria-hidden="true">19.13.</strong> SSRF</a></li><li class="chapter-item "><a href="../../posts/ctf/1.6_OS command injection.html"><strong aria-hidden="true">19.14.</strong> OS command injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.7_Local file inclusion.html"><strong aria-hidden="true">19.15.</strong> Local file inclusion</a></li><li class="chapter-item "><a href="../../posts/ctf/1.8_Remote file inclusion.html"><strong aria-hidden="true">19.16.</strong> Remote file inclusion</a></li><li class="chapter-item "><a href="../../posts/ctf/1.9_CSRFm.html"><strong aria-hidden="true">19.17.</strong> CSRF</a></li><li class="chapter-item "><a href="../../posts/ctf/1.10_NoSQL injection.html"><strong aria-hidden="true">19.18.</strong> NoSQL injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.11_JSON injection.html"><strong aria-hidden="true">19.19.</strong> JSON injection</a></li><li class="chapter-item "><a href="../../posts/ctf/1.12_CTF_Web_SQL_Note.html"><strong aria-hidden="true">19.20.</strong> CTF Web SQL Note</a></li><li class="chapter-item "><a href="../../posts/ctf/2.1_XXE.html"><strong aria-hidden="true">19.21.</strong> XXE</a></li><li class="chapter-item "><a href="../../posts/ctf/2.2_XSS.html"><strong aria-hidden="true">19.22.</strong> XSS</a></li><li class="chapter-item "><a href="../../posts/ctf/2.3_Upload File.html"><strong aria-hidden="true">19.23.</strong> Upload File</a></li><li class="chapter-item "><a href="../../posts/ctf/2.4_serialize_unserialize.html"><strong aria-hidden="true">19.24.</strong> serialize unserialize</a></li><li class="chapter-item "><a href="../../posts/ctf/2.5_Race condition.html"><strong aria-hidden="true">19.25.</strong> Race condition</a></li><li class="chapter-item "><a href="../../posts/ctf/3.2_PWN_note.html"><strong aria-hidden="true">19.26.</strong> PWN_note</a></li><li class="chapter-item "><a href="../../posts/ctf/3.3_pwn HCTF2016 brop.html"><strong aria-hidden="true">19.27.</strong> pwn HCTF2016 brop</a></li><li class="chapter-item "><a href="../../posts/ctf/pwn_patch_defense_skill.html"><strong aria-hidden="true">19.28.</strong> PWN Patch defense skill</a></li><li class="chapter-item "><a href="../../posts/ctf/pwn_stack_overflow.html"><strong aria-hidden="true">19.29.</strong> PWN stack overflow</a></li><li class="chapter-item "><a href="../../posts/ctf/pwn_heap_overflow.html"><strong aria-hidden="true">19.30.</strong> PWN heap overflow</a></li><li class="chapter-item "><a href="../../posts/ctf/pwn_format_string_vulnerability.html"><strong aria-hidden="true">19.31.</strong> PWN Format String Vulnerability</a></li><li class="chapter-item "><a href="../../posts/ctf/kali_linux_tutorials.html"><strong aria-hidden="true">19.32.</strong> Kali linux tutorials</a></li><li class="chapter-item "><a href="../../posts/ctf/google_dorks_2023_lists.html"><strong aria-hidden="true">19.33.</strong> Google Dorks 2023 Lists</a></li><li class="chapter-item "><a href="../../posts/ctf/dvwa_writeup.html"><strong aria-hidden="true">19.34.</strong> DVWA WriteUp</a></li><li class="chapter-item "><a href="../../posts/ctf/bwapp_writeup.html"><strong aria-hidden="true">19.35.</strong> bWAPP WriteUp</a></li><li class="chapter-item "><a href="../../posts/ctf/sqlilabs_writeup.html"><strong aria-hidden="true">19.36.</strong> sqlilabs WriteUp</a></li><li class="chapter-item "><a href="../../posts/ctf/ctf_train_at_hangzhou.html"><strong aria-hidden="true">19.37.</strong> ctf train at hangzhou</a></li><li class="chapter-item "><a href="../../posts/ctf/ctf_common_mindmap_list.html"><strong aria-hidden="true">19.38.</strong> ctf common mindmap list</a></li><li class="chapter-item "><a href="../../posts/ctf/error_based_sql_injection.html"><strong aria-hidden="true">19.39.</strong> Error Based SQL Injection</a></li><li class="chapter-item "><a href="../../posts/ctf/urlfinder_tutorial.html"><strong aria-hidden="true">19.40.</strong> URLFinder Tutorial</a></li><li class="chapter-item "><a href="../../posts/ctf/observer_ward_tutorial.html"><strong aria-hidden="true">19.41.</strong> observer_ward Tutorial</a></li><li class="chapter-item "><a href="../../posts/ctf/mysql_udf_.html"><strong aria-hidden="true">19.42.</strong> MySQL UDF 提权</a></li><li class="chapter-item "><a href="../../posts/ctf/nuclei__tutorial.html"><strong aria-hidden="true">19.43.</strong> Nuclei Tutorial</a></li><li class="chapter-item "><a href="../../posts/ctf/2024_ctf_solution_thinking.html"><strong aria-hidden="true">19.44.</strong> 2024 ctf solution thinking</a></li><li class="chapter-item "><a href="../../posts/ctf/man_che_si_te_bian_ma.html"><strong aria-hidden="true">19.45.</strong> 曼彻斯特编码</a></li></ol></li></ol>
|
||
</div>
|
||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||
<div class="sidebar-resize-indicator"></div>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- Track and set sidebar scroll position -->
|
||
<script>
|
||
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
|
||
sidebarScrollbox.addEventListener('click', function(e) {
|
||
if (e.target.tagName === 'A') {
|
||
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
|
||
}
|
||
}, { passive: true });
|
||
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
|
||
sessionStorage.removeItem('sidebar-scroll');
|
||
if (sidebarScrollTop) {
|
||
// preserve sidebar scroll position when navigating via links within sidebar
|
||
sidebarScrollbox.scrollTop = sidebarScrollTop;
|
||
} else {
|
||
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
|
||
var activeSection = document.querySelector('#sidebar .active');
|
||
if (activeSection) {
|
||
activeSection.scrollIntoView({ block: 'center' });
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<div id="page-wrapper" class="page-wrapper">
|
||
|
||
<div class="page">
|
||
<div id="menu-bar-hover-placeholder"></div>
|
||
<div id="menu-bar" class="menu-bar sticky">
|
||
<div class="left-buttons">
|
||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||
<i class="fa fa-bars"></i>
|
||
</label>
|
||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||
<i class="fa fa-paint-brush"></i>
|
||
</button>
|
||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||
</ul>
|
||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||
<i class="fa fa-search"></i>
|
||
</button>
|
||
</div>
|
||
|
||
<h1 class="menu-title">Andrew's Blog</h1>
|
||
|
||
<div class="right-buttons">
|
||
<a href="https://gitlink.org.cn/dnrops/dnrops.gitlink.net.git" title="Git repository" aria-label="Git repository">
|
||
<i id="git-repository-button" class="fa fa-github"></i>
|
||
</a>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<div id="search-wrapper" class="hidden">
|
||
<form id="searchbar-outer" class="searchbar-outer">
|
||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||
</form>
|
||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||
<div id="searchresults-header" class="searchresults-header"></div>
|
||
<ul id="searchresults">
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||
<script>
|
||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||
});
|
||
</script>
|
||
|
||
<div id="content" class="content">
|
||
<main>
|
||
<h1 id="创建自定义指令"><a class="header" href="#创建自定义指令">创建自定义指令</a></h1>
|
||
<p>这是一篇译文,来自angular开发者说明的指令。主要面向已经熟悉angular开发基础的开发者。这篇文档解释了什么情况下需要创建自己的指令,和如何去创建指令。</p>
|
||
<h2 id="什么是指令"><a class="header" href="#什么是指令">什么是指令</a></h2>
|
||
<p>从一个高的层面来讲,指令是angular $compile服务的说明,当特定的标签(属性,元素名,或者注释)
|
||
出现在DOM中的时候,它让编译器附加指定的行为到DOM上。
|
||
这个过程是很简单的。angular内部有很用这样自带的指令,比如说ngBind,
|
||
ngView,就像你创建控制器和服务一样,你也可以创建自己的指令。当angular启动的时候,Angular的编译器分析html去匹配指令,这允许指令注册行为或者改变DOM。</p>
|
||
<h3 id="匹配指令"><a class="header" href="#匹配指令">匹配指令</a></h3>
|
||
<p>在写指令之前,我们首先需要知道的是angular是如何匹配到一个指令的,在以下的例子我们说input元素匹配到ngModel指令.</p>
|
||
<pre><code class="language-javascript"><input ng-model="foo">
|
||
</code></pre>
|
||
<p>下面这种方法同样也会匹配到ngModel:</p>
|
||
<pre><code class="language-javascript"><input data-ng:model="foo">
|
||
</code></pre>
|
||
<p>Angular会规范化一个元素的标签名和属性名,以便确定哪一个元素匹配到哪一个指令。我们在js中是通过使用规范化后的驼峰式的名字来引用指令(比如ngModel)。在HTML中常常使用’-‘划定的属性名字来调用指令(比如说ng-model).
|
||
规范化的处理过程: -去掉元素或属性上面的x-和data-的前缀 -转化’:‘,’-’和‘_-’形式的命名为驼峰式拼写
|
||
以下例子展示了用不同的方式匹配到ngBind指令</p>
|
||
<pre><code class="language-javascript"><span ng-bind="name"></span> <br/>
|
||
<span ng:bind="name"></span> <br/>
|
||
<span ng_bind="name"></span> <br/>
|
||
<span data-ng-bind="name"></span> <br/>
|
||
<span x-ng-bind="name"></span> <br/>
|
||
</code></pre>
|
||
<p>Best Practice:
|
||
优先使用’-’格式的命名(比如说ng-bind匹配ngBind)。如果你想在HTML验证工具中通过,你可以用’data-’前缀的方式(比如data-ng-bind)。其他格式的命名是因为历史遗留的原因存在,避免使用它们。
|
||
$compile服务可以基于元素的名字,属性名,类名,和注释来匹配指令
|
||
所有Angular内部提供的指令都匹配属性名,标签名,注释,或者类名。以下不同的方式都可以被解析到</p>
|
||
<pre><code class="language-javascript"><my-dir></my-dir>
|
||
<span my-dir="exp"></span>
|
||
<!-- directive: my-dir exp -->
|
||
<span class="my-dir: exp;"></span>
|
||
</code></pre>
|
||
<p>Best Practice: 优先利用标签名和属性名的方式使用指令。这样子更容易理解指定的元素匹配到了哪个元素。
|
||
Best Practice: 注释的方式通常被用在DOM
|
||
API限制创建跨越多个元素的指令,比如说table元素,限制重复嵌套,这样就要用注释的方式。在AngularJS
|
||
1.2版本中,通过使用ng-repeat-start 和 ng-repeat-end 作为一个更好的方案来解决这个问题。在可能的情况下,推荐使用这种方式。</p>
|
||
<h4 id="文本和属性的绑定"><a class="header" href="#文本和属性的绑定">文本和属性的绑定</a></h4>
|
||
<p>在编译过程中,编译器会使用$interpolate服务来检测匹配到的文本和属性值是否包含内嵌表达式。这些表达式被注册为watches,可以在digest循环时被更新。</p>
|
||
<pre><code class="language-javascript"><a ng-href="img/{{username}}.jpg">Hello {{ username }}!</a>;
|
||
</code></pre>
|
||
<h4 id="ngattr属性的绑定"><a class="header" href="#ngattr属性的绑定">ngAttr属性的绑定</a></h4>
|
||
<p>浏览器有些时候会对它认为合法的属性值非常的挑剔(就是某些元素的属性是不可以任意赋值的,否则会报错)。
|
||
比如:</p>
|
||
<pre><code class="language-javascript"><svg>
|
||
<circle cx="{{cx}}"></circle>
|
||
</svg>;
|
||
</code></pre>
|
||
<p>使用这样的写法时,我们会发现控制台中报错<code>Error: Invalid value for attribute cx=“{{cx}}”.
|
||
</code>.这是由于SVG DOM API的限制,你不能简单的写为<code>cx=“{{cx}}”</code>.
|
||
使用ng-attr-cx 可以解决这个问题
|
||
如果一个绑定的属性使用ngAttr前缀(或者ng-attr),
|
||
那么在绑定的时候将会被应用到相应的未前缀化的属性,这种方式允许你绑定到需要马上被浏览器处理的属性上面(比如SVG元素的circle[cx]属性)。
|
||
所以,我们可以这样写来修复以上的问题:</p>
|
||
<pre><code class="language-javascript"><svg>
|
||
<circle ng-attr-cx="{{cx}}"></circle>
|
||
</svg>;
|
||
</code></pre>
|
||
<h2 id="创建指令"><a class="header" href="#创建指令">创建指令</a></h2>
|
||
<p>首先我们来谈论下注册指令的API,跟controller一样,指令是注册在module上,不同的是,指令是通过module.directive
|
||
API来注册的。module.directive接受的是一个规范化的名字和工厂函数,这个工厂函数返回一个包含不同配置的对象,这个对象用来告诉$compile服务如何进行下一步处理。
|
||
工厂函数仅在编译器第一次匹配到指令的时候调用一次。通常在工厂函数中执行初始化的工作。该函数使用$injector.invoke调用,所以它可以像controller一样进行依赖注入。
|
||
Best Practice: 优先返回一个定义好的对象,而不是返回一个函数。
|
||
接下来,我们先会了解一些常见的例子,然后再深入了解不同的配置项的原理和编译过程。
|
||
Best Practice:
|
||
为了避免与某些未来的标准命名冲突,最好前缀化你自己的指令,比如你创建一个<carousel>指令,它可能会产生冲突,加入HTML7引入相同的元素。推荐使用两三个单词的前缀(比如btfCarousel),同样不能使用ng或者其他可能与angular未来版本起冲突的前缀。
|
||
以下的例子中,我们统一使用my前缀。</p>
|
||
<h3 id="模板扩展的指令"><a class="header" href="#模板扩展的指令">模板扩展的指令</a></h3>
|
||
<p>当你有大量代表客户信息的模板。这个模板在你的代码中重复了很多次,当你改变一个地方的时候,你不得不在其他地方同时改动,这时候,你就要使用指令来简化你的模板。
|
||
我们来创建一个指令,简单的时候静态模板来替换它的内容。</p>
|
||
<pre><code class="language-javascript"><div ng-controller="Ctrl">
|
||
<div my-customer></div>
|
||
</div>;
|
||
// JS
|
||
angular.module("docsSimpleDirective", [])
|
||
.controller("Ctrl", function ($scope) {
|
||
$scope.customer = {
|
||
name: "Naomi",
|
||
address: "1600 Amphitheatre",
|
||
};
|
||
})
|
||
.directive("myCustomer", function () {
|
||
return {
|
||
template: "Name: {{customer.name}} Address: {{customer.address}}",
|
||
};
|
||
});
|
||
</code></pre>
|
||
<p>注意我们这里做了一些绑定,$compile编译很链接<div my-customer></p>
|
||
</div>之后,它将会匹配子元素的指令。这意味着你可以组合一些指令。以下例子中你会看到如何做到这一点。
|
||
这个例子中,我们直接在template配置项里写上模板,但是随着模板大小的增加,这样非常不优雅。
|
||
Best Practice: 除非你的模板非常小,否则更好的是分割成单独的hmtl文件,然后使用templateUrl选项来加载。
|
||
加入你熟悉ngInclude,templateUrl跟它非常类似。现在我们使用templateUrl方式重写上面的例子:
|
||
```javascript
|
||
<div ng-controller="Ctrl">
|
||
<div my-customer></div>
|
||
</div>;
|
||
// JS
|
||
angular.module("docsTemplateUrlDirective", [])
|
||
.controller("Ctrl", function ($scope) {
|
||
$scope.customer = {
|
||
name: "Naomi",
|
||
address: "1600 Amphitheatre",
|
||
};
|
||
})
|
||
.directive("myCustomer", function () {
|
||
return {
|
||
templateUrl: "my-customer.html",
|
||
};
|
||
});
|
||
// my-customer.html
|
||
Name: {
|
||
{
|
||
customer.name;
|
||
}
|
||
}
|
||
Address: {
|
||
{
|
||
customer.address;
|
||
}
|
||
}
|
||
```
|
||
非常好,但是如果我们想让我们的指令匹配标签名<my-customer>? 如果我们只是简单的把<my-customer>元素放在hmtl上面,会发现没有效果。
|
||
Note: 创建指令的时候,默认仅使用属性的方式。为了创建一个能由元素名字触发的指令,你需要用到restrict配置。
|
||
restrict配置可以按如下方式设置: -'A' 仅匹配属性名字 -'E' 仅匹配元素名字 -'AE' 可以匹配到属性名字或者元素名
|
||
所以,我们可以使用 <code>restrict: 'E'</code>配置我们指令。
|
||
```javascript
|
||
<div ng-controller="Ctrl">
|
||
<div my-customer></div>
|
||
</div>;
|
||
// JS
|
||
angular.module("docsTemplateUrlDirective", [])
|
||
.controller("Ctrl", function ($scope) {
|
||
$scope.customer = {
|
||
name: "Naomi",
|
||
address: "1600 Amphitheatre",
|
||
};
|
||
})
|
||
.directive("myCustomer", function () {
|
||
return {
|
||
restrict: "E",
|
||
templateUrl: "my-customer.html",
|
||
};
|
||
});
|
||
// my-customer.html
|
||
Name: {
|
||
{
|
||
customer.name;
|
||
}
|
||
}
|
||
Address: {
|
||
{
|
||
customer.address;
|
||
}
|
||
}
|
||
```
|
||
点击查看更多关于<a>restrictJ</a>信息
|
||
Note: 什么时候使用属性名或元素名呢? 当创建一个含有自己模板的组件的时候,需要使用元素名,如果仅仅是为已有的元素添加功能的话,使用属性名。
|
||
使用元素名做为myCustomer指令是非常正确的决定,因为你不是用一些'customer'行为来点缀元素,而是定义一个具有自己行为的元素作为customer组件。
|
||
###隔离指令的作用域 上面我们的myCustomer指令已经非常好了,但是它有个致命的缺陷,我们在给定的作用域内仅能使用一次。
|
||
它现在的实现是,我们每次重用该指令的时候都要为它新创一个控制器。
|
||
```javascript
|
||
(
|
||
<div ng-controller="NaomiCtrl">
|
||
<my-customer></my-customer>
|
||
</div>
|
||
) <
|
||
hr >
|
||
(
|
||
<div ng-controller="IgorCtrl">
|
||
<my-customer></my-customer>
|
||
</div>
|
||
);
|
||
// JS
|
||
angular.module("docsScopeProblemExample", [])
|
||
.controller("NaomiCtrl", function ($scope) {
|
||
$scope.customer = {
|
||
name: "Naomi",
|
||
address: "1600 Amphitheatre",
|
||
};
|
||
})
|
||
.controller("IgorCtrl", function ($scope) {
|
||
$scope.customer = {
|
||
name: "Igor",
|
||
address: "123 Somewhere",
|
||
};
|
||
})
|
||
.directive("myCustomer", function () {
|
||
return {
|
||
restrict: "E",
|
||
templateUrl: "my-customer.html",
|
||
};
|
||
});
|
||
// my-customer.html
|
||
Name: {
|
||
{
|
||
customer.name;
|
||
}
|
||
}
|
||
Address: {
|
||
{
|
||
customer.address;
|
||
}
|
||
}
|
||
```
|
||
这很明显不是一个好的解决方案。
|
||
我们想要做的是能够把指令的作用域与外部的作用域隔离开来,然后映射外部的作用域到指令内部的作用域。可以通过创建isolate
|
||
scope来完成这个目的。这样的话,我们使用指令的scope配置。
|
||
```javascript
|
||
<div ng-controller="Ctrl">
|
||
<my-customer customer="naomi"></my-customer>
|
||
<hr>
|
||
<my-customer customer="igor"></my-customer>
|
||
</div>
|
||
// JS
|
||
angular.module('docsIsolateScopeDirective', [])
|
||
.controller('Ctrl', function($scope) {
|
||
$scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
|
||
$scope.igor = { name: 'Igor', address: '123 Somewhere' };
|
||
})
|
||
.directive('myCustomer', function() {
|
||
return {
|
||
restrict: 'E',
|
||
scope: {
|
||
customer: '=customer'
|
||
},
|
||
templateUrl: 'my-customer.html'
|
||
};
|
||
});
|
||
// my-customer.html
|
||
Name: {{customer.name}} Address: {{customer.address}}
|
||
```
|
||
首先看hmtl,第一个<my-customer>绑定内部作用域的customer到naomi。这个naomi我们在控制器中已经定义好了。第二个是绑定customer到igor。
|
||
现在看看scope是如何配置的。
|
||
```javascript
|
||
//...
|
||
scope: {
|
||
customer: '=customer'
|
||
},
|
||
//...
|
||
```
|
||
属性名(customer)是myCustomer指令上isolated scope的变量名。它的值(=customer)告诉编译器绑定到customer属性。
|
||
Note:
|
||
指令作用域配置中的'=attr'属性名是被规范化过后的名字,比如要绑定<div bind-to-this="thing">中的属性,你就要使用'=bindToThis'的绑定。
|
||
对于属性名和你想要绑定的值的名字一样,你可以使用这样的快捷语法:
|
||
```javascript
|
||
//...
|
||
scope: {
|
||
// same as '=customer'
|
||
customer: '='
|
||
},
|
||
//...
|
||
```
|
||
使用isolated scope还有另外一个用处,那就是可以绑定不同的数据到指令内部的作用域。
|
||
在我们的例子中,我们可以添加另外一个属性vojta到我们的作用域,然后在我们的指令模板中访问它。
|
||
```javascript
|
||
<div ng-controller="Ctrl">
|
||
<my-customer customer="naomi"></my-customer>
|
||
</div>
|
||
//JS
|
||
angular.module('docsIsolationExample', [])
|
||
.controller('Ctrl', function($scope) {
|
||
$scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
|
||
$scope.vojta = { name: 'Vojta', address: '3456 Somewhere Else' };
|
||
})
|
||
.directive('myCustomer', function() {
|
||
return {
|
||
restrict: 'E',
|
||
scope: {
|
||
customer: '=customer'
|
||
},
|
||
templateUrl: 'my-customer-plus-vojta.html'
|
||
};
|
||
});
|
||
//my-customer-plus-vojta.html
|
||
Name: {{customer.name}} Address: {{customer.address}}
|
||
<hr>
|
||
Name: {{vojta.name}} Address: {{vojta.address}}
|
||
```
|
||
注意到,{{vojta.name}}和{{vojta.address}}都是空的,意味着他们是undefined,
|
||
虽然我们在控制器中定义了vojta,但是在指令内部访问不到。
|
||
就像它的名字暗示的一样, 指令的isolate
|
||
scope隔离了除了你添加到作用域:{}对象中的数据模型外的一切东西。这对于你要建立一个可重复使用的组件是非常有用的,因为它阻止了除了你想要传入的数据模型外其他东西改变你数据模型的状态。
|
||
Note: 正常情况下,作用域是原型继承自父作用域。但是isolate scope没有这样的继承。
|
||
Best Practice: 当你想要使你的组件在应用范围内可重用,那么使用scope配置去创建一个isolate scopes
|
||
###创建一个操作DOM的指令 在这个例子中,我们会创建一个显示当前时间的指令,每秒一次更新DOM以正确的显示当前的时间。
|
||
指令修改DOM通常是在link配置中,link选项接受一个带有如下标签的函数<code>function link(scope,element,attrs)
|
||
{...}</code>其中: -<code>scope</code>是angular scope对象
|
||
-<code>element</code>指令匹配的jqLite封装的元素(angular内部实现的类jquery的库)
|
||
-<code>attrs</code>是一个带有规范化后属性名字和相应值的对象
|
||
在我们的link函数中,我们更新显示时间每秒一次,或者当用户改变指定绑定的时间格式字符串的时候。我们也要移除定时器,当指令被删除的时候,以避免引入内存泄露。
|
||
```javascript
|
||
<div ng-controller="Ctrl2">
|
||
Date format: <input ng-model="format"> <hr/>
|
||
Current time is: <span my-current-time="format"></span>
|
||
</div>
|
||
//JS
|
||
angular.module('docsTimeDirective', [])
|
||
.controller('Ctrl2', function($scope) {
|
||
$scope.format = 'M/d/yy h:mm:ss a';
|
||
})
|
||
.directive('myCurrentTime', function($timeout, dateFilter) {
|
||
<p>function link(scope, element, attrs) {
|
||
var format,
|
||
timeoutId;</p>
|
||
<p>function updateTime() {
|
||
element.text(dateFilter(new Date(), format));
|
||
}</p>
|
||
<p>scope.$watch(attrs.myCurrentTime, function(value) {
|
||
format = value;
|
||
updateTime();
|
||
});</p>
|
||
<p>function scheduleUpdate() {
|
||
// save the timeoutId for canceling
|
||
timeoutId = $timeout(function() {
|
||
updateTime(); // update DOM
|
||
scheduleUpdate(); // schedule the next update
|
||
}, 1000);
|
||
}</p>
|
||
<p>element.on(‘$destroy’, function() {
|
||
$timeout.cancel(timeoutId);
|
||
});</p>
|
||
<p>// start the UI update process.
|
||
scheduleUpdate();
|
||
}</p>
|
||
<p>return {
|
||
link: link
|
||
};
|
||
});</p>
|
||
<pre><code>这里有很多东西值得注意的,就像module.controller API,
|
||
module.directive中函数参数是依赖注入,因此,我们可以在Link函数内部使用$timeout和dataFilter服务。
|
||
我们注册了一个事件<code>element.on('$destroy', ...)</code>,
|
||
是什么触发了这个<code>$destory</code>事件呢?
|
||
AngularJS会触发一些特定的事件,当一个被angular编译过的DOM元素被移除的时候,它会触发一个<code>$destory</code>事件,同样的,当一个angular作用域被移除的时候,它会向下广播<code>$destory</code>事件到所有监听的作用域。
|
||
通过监听事件,你可以移除可能引起内存泄露的事件监听器,注册在元素和作用域上的监听器在它们被移除的时候,会自动会清理掉,但是假如注册一个事件在服务或者没有被删除的DOM节点上,你就必须手工清理,否则会有内存泄露的风险。
|
||
Best Practice:执行被移除的时候应该做一些清理的操作, 可以使用<code>element.on('$destroy',
|
||
...)</code>或者<code>scope.on('$destroy', ...)</code>来运行解除绑定的函数,
|
||
### 创建包裹其他元素的指令
|
||
我们现在已经实现了,使用isolate
|
||
scopes传递数据模型到指令里面。但是有时候我们需要能够传递一整个模板而不是字符串或者对象。让我们通过创建'dialog
|
||
box'组件来说明。这个'dialog box'组件应该能够包裹任意内容。
|
||
要实现这个,我们使用transclude配置
|
||
```javascript
|
||
<div ng-controller="Ctrl">
|
||
<my-dialog>Check out the contents, {{ name }}!</my-dialog>
|
||
</div>;
|
||
//JS
|
||
angular.module("docsTransclusionDirective", [])
|
||
.controller("Ctrl", function ($scope) {
|
||
$scope.name = "Tobias";
|
||
})
|
||
.directive("myDialog", function () {
|
||
return {
|
||
restrict: "E",
|
||
transclude: true,
|
||
templateUrl: "my-dialog.html",
|
||
};
|
||
});
|
||
//my-dialog.html
|
||
<div class="alert" ng-transclude>
|
||
</div>;
|
||
</code></pre>
|
||
<p>这个transclude配置用来干嘛呢? transclude使带有这个配置的指令的内容能够访问指令外部的作用域。
|
||
参照以下例子,注意到我们增加了一个link函数,在这个link函数内部我们重定义了name属性的值为Jeff,那么现在这个{{name}}会被解析成哪个值呢?</p>
|
||
<pre><code class="language-javascript"><div ng-controller="Ctrl">
|
||
<my-dialog>Check out the contents, {{ name }}!</my-dialog>
|
||
</div>;
|
||
//JS
|
||
angular.module("docsTransclusionDirective", [])
|
||
.controller("Ctrl", function ($scope) {
|
||
$scope.name = "Tobias";
|
||
})
|
||
.directive("myDialog", function () {
|
||
return {
|
||
restrict: "E",
|
||
transclude: true,
|
||
templateUrl: "my-dialog.html",
|
||
link: function (element, scope) {
|
||
scope.name = "Jeff";
|
||
},
|
||
};
|
||
});
|
||
//my-dialog.html
|
||
<div class="alert" ng-transclude>
|
||
</div>;
|
||
</code></pre>
|
||
<p>一般,我们会认为{{name}}会被解析为Jeff,然而这里,我们看到这个例子中的{{name}}还是被解析成了Tobias.
|
||
transclude配置改变了指令相互嵌套的方式,他使指令的内容拥有任何指令外部的作用域,而不是内部的作用域。为了实现这个,它给指令内容一次访问外部作用域的机会。
|
||
这样的行为对于包裹内容的指令是非常有意义的。因为如果不这样的话,你就必须分别传入每个你需要使用的数据模型。如果你需要传入每个要使用的数据模型,那么你就无法做到适应各种不同内容的情况。
|
||
Best Practice: 仅当你要创建一个包裹任意内容的指令的时候使用transclude:true。
|
||
下一步,我们增加一个按钮到’dialog box’组件里面,允许用户使用指令绑定自己定义的行为。</p>
|
||
<pre><code class="language-javascript"><div ng-controller="Ctrl">
|
||
<my-dialog ng-hide="dialogIsHidden" on-close="dialogIsHidden = true">
|
||
Check out the contents, {{ name }}!
|
||
</my-dialog>
|
||
</div>;
|
||
//JS
|
||
angular.module("docsIsoFnBindExample", [])
|
||
.controller("Ctrl", function ($scope, $timeout) {
|
||
$scope.name = "Tobias";
|
||
$scope.hideDialog = function () {
|
||
$scope.dialogIsHidden = true;
|
||
$timeout(function () {
|
||
$scope.dialogIsHidden = false;
|
||
}, 2000);
|
||
};
|
||
})
|
||
.directive("myDialog", function () {
|
||
return {
|
||
restrict: "E",
|
||
transclude: true,
|
||
scope: {
|
||
"close": "&onClose",
|
||
},
|
||
templateUrl: "my-dialog-close.html",
|
||
};
|
||
});
|
||
//my-dialog-close.html
|
||
<div class="alert">
|
||
<a href class="close" ng-click="close()">&times;</a>
|
||
<div ng-transclude></div>
|
||
</div>;
|
||
</code></pre>
|
||
<p>我们想要通过在指令的作用域上调用,来运行我们传递进去的函数,但是这个函数是运行在定义时候的上下文(js通常都是这样子的)。
|
||
先前我们看到如何scope配置使用’=prop’,但是在上文的例子中,我们使用’&prop’,’&‘绑定开放了一个函数到isolated scope,允许
|
||
isolated
|
||
scope调用它,同时维持原来函数的作用域(这里的作用域都是指$scope)。所以当一个用户点击x时候,就会运行Ctrl控制器的close函数。
|
||
Best Practice: 当你的指令想要开放一个API去绑定特定的行为,在scope配置中使用’&prop’.
|
||
###创建一个添加事件监听器的指令 先前,我们使用link函数创建一个操作DOM元素的指令,基于上面的例子,我们创建一个在元素上添加事件监听的指令。
|
||
举个例子,假如我们想要创建一个让用户可拖拽的元素,该怎么做呢?</p>
|
||
<pre><code class="language-javascript"><span my-draggable>Drag ME</span>;
|
||
//JS
|
||
angular.module("dragModule", [])
|
||
.directive("myDraggable", function ($document) {
|
||
return function (scope, element, attr) {
|
||
var startX = 0, startY = 0, x = 0, y = 0;
|
||
element.css({
|
||
position: "relative",
|
||
border: "1px solid red",
|
||
backgroundColor: "lightgrey",
|
||
cursor: "pointer",
|
||
});
|
||
element.on("mousedown", function (event) {
|
||
// Prevent default dragging of selected content
|
||
event.preventDefault();
|
||
startX = event.screenX - x;
|
||
startY = event.screenY - y;
|
||
$document.on("mousemove", mousemove);
|
||
$document.on("mouseup", mouseup);
|
||
});
|
||
function mousemove(event) {
|
||
y = event.screenY - startY;
|
||
x = event.screenX - startX;
|
||
element.css({
|
||
top: y + "px",
|
||
left: x + "px",
|
||
});
|
||
}
|
||
function mouseup() {
|
||
$document.unbind("mousemove", mousemove);
|
||
$document.unbind("mouseup", mouseup);
|
||
}
|
||
};
|
||
});
|
||
</code></pre>
|
||
<h3 id="创建相互通信的指令"><a class="header" href="#创建相互通信的指令">创建相互通信的指令</a></h3>
|
||
<p>你可以通过在模板使用指令来组合任何指令。 有时候,你想要一个指令从其他的指令上面创建 想象你想要一个带有tab的容器,容器的内容对应于激活的tab。</p>
|
||
<pre><code class="language-javascript"><my-tabs>
|
||
<my-pane title="Hello">
|
||
<h5 id="creating-custom-directives_source_hello">Hello</h5>
|
||
<p>Lorem ipsum dolor sit amet</p>
|
||
</my-pane>
|
||
<my-pane title="World">
|
||
<h5 id="creating-custom-directives_source_world">World</h5>
|
||
<em>Mauris elementum elementum enim at suscipit.</em>
|
||
<p><a href ng-click="i = i + 1">counter: {{i || 0}}</a></p>
|
||
</my-pane>
|
||
</my-tabs>
|
||
//JS
|
||
angular.module('docsTabsExample', [])
|
||
.directive('myTabs', function() {
|
||
return {
|
||
restrict: 'E',
|
||
transclude: true,
|
||
scope: {},
|
||
controller: function($scope) {
|
||
var panes = $scope.panes = [];
|
||
|
||
$scope.select = function(pane) {
|
||
angular.forEach(panes, function(pane) {
|
||
pane.selected = false;
|
||
});
|
||
pane.selected = true;
|
||
};
|
||
|
||
this.addPane = function(pane) {
|
||
if (panes.length == 0) {
|
||
$scope.select(pane);
|
||
}
|
||
panes.push(pane);
|
||
};
|
||
},
|
||
templateUrl: 'my-tabs.html'
|
||
};
|
||
})
|
||
.directive('myPane', function() {
|
||
return {
|
||
require: '^myTabs',
|
||
restrict: 'E',
|
||
transclude: true,
|
||
scope: {
|
||
title: '@'
|
||
},
|
||
link: function(scope, element, attrs, tabsCtrl) {
|
||
tabsCtrl.addPane(scope);
|
||
},
|
||
templateUrl: 'my-pane.html'
|
||
};
|
||
});
|
||
//my-tabs.html
|
||
<div class="tabbable">
|
||
<ul class="nav nav-tabs">
|
||
<li ng-repeat="pane in panes" ng-class="{active:pane.selected}">
|
||
<a href="" ng-click="select(pane)">{{pane.title}}</a>
|
||
</li>
|
||
</ul>
|
||
<div class="tab-content" ng-transclude></div>
|
||
</div>
|
||
//my-pane.html
|
||
<div class="tab-pane" ng-show="selected" ng-transclude>
|
||
</div>
|
||
</code></pre>
|
||
<p>myPane指令有一个require:‘^myTabs’的配置,当指令使用这个配置,$compile服务叫myTabs的指令并获取它的控制器实例,如果没有找到,将会抛出一个错误。’^‘前缀意味着指令在它的父元素上面搜索控制器(没有’^’前缀的话,指令默认会在自身的元素上面搜索指定的指令)。
|
||
这里myTabs的控制器是来自何处呢?通过使用controller配置可以为指令指定一个控制器,
|
||
上问例子中myTab就是使用这个配置。就像ngController, 这个配置为指令的模板绑定了一个控制器。
|
||
再看我们的myPane’s定义,注意到link函数的最后一个参数:
|
||
tabCtrl,当一个指令包含另一个指令(通过require方式),它会接收该指令的控制器实例作为link函数的第四个参数,利用这个,myPane可以调用myTabs的addPane函数。
|
||
精明的读者可能想知道link跟controller之间的区别,最基本的区别就是控制器开放一个API(就是这个控制器实例可以被其他实例读取到),link函数可以通过require与控制器交互。
|
||
Best Practice: 当你要开放一个API给其他指令的时候使用控制器,否则使用link函数。</p>
|
||
<h3 id="总结"><a class="header" href="#总结">总结</a></h3>
|
||
<p>这里我们讲解了一个些指令的主要使用案例。每一个都可以作为你创建自己指令的很好的起点。
|
||
如果你想更深入的了解编译的处理过程,可以查看<a>compiler guide</a>
|
||
<a>$compile API</a>页面有directive每个配置项的具体解释,可以参阅。</p>
|
||
|
||
</main>
|
||
|
||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||
<!-- Mobile navigation buttons -->
|
||
<a rel="prev" href="../../posts/angular/controller_communication.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||
<i class="fa fa-angle-left"></i>
|
||
</a>
|
||
|
||
<a rel="next prefetch" href="../../posts/angular/directive_notes.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||
<i class="fa fa-angle-right"></i>
|
||
</a>
|
||
|
||
<div style="clear: both"></div>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
|
||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||
<a rel="prev" href="../../posts/angular/controller_communication.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||
<i class="fa fa-angle-left"></i>
|
||
</a>
|
||
|
||
<a rel="next prefetch" href="../../posts/angular/directive_notes.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||
<i class="fa fa-angle-right"></i>
|
||
</a>
|
||
</nav>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<script>
|
||
window.playground_line_numbers = true;
|
||
</script>
|
||
|
||
<script>
|
||
window.playground_copyable = true;
|
||
</script>
|
||
|
||
<script src="../../ace.js"></script>
|
||
<script src="../../editor.js"></script>
|
||
<script src="../../mode-rust.js"></script>
|
||
<script src="../../theme-dawn.js"></script>
|
||
<script src="../../theme-tomorrow_night.js"></script>
|
||
|
||
<script src="../../elasticlunr.min.js"></script>
|
||
<script src="../../mark.min.js"></script>
|
||
<script src="../../searcher.js"></script>
|
||
|
||
<script src="../../clipboard.min.js"></script>
|
||
<script src="../../highlight.js"></script>
|
||
<script src="../../book.js"></script>
|
||
|
||
<!-- Custom JS scripts -->
|
||
<script src="../../src/js/custom.js"></script>
|
||
|
||
|
||
</div>
|
||
</body>
|
||
</html>
|